diff --git a/.gitattributes b/.gitattributes index 30cc9de8f1..5c7f5b73b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,5 @@ * text eol=lf -# VSTool (normalization disabled) -indra/tools/vstool/* -text - # Images *.bmp binary *.BMP binary diff --git a/.github/workflows/qatest.yaml b/.github/workflows/qatest.yaml index 4e10900441..457e915726 100644 --- a/.github/workflows/qatest.yaml +++ b/.github/workflows/qatest.yaml @@ -43,7 +43,11 @@ jobs: artifact: Windows-installer install-path: 'C:\viewer-automation-main' - os: windows - runner: qa-dan-asus + runner: qa-windows-asus-dan + artifact: Windows-installer + install-path: 'C:\viewer-automation-main' + - os: windows + runner: qa-windows-z600-dan artifact: Windows-installer install-path: 'C:\viewer-automation-main' - os: mac diff --git a/autobuild.xml b/autobuild.xml index 2f2ab55012..ffea5916e9 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1435,11 +1435,11 @@ creds github hash - 9e59c93c7110e87b4ff3db330f11a23c50e5000f + 7facda95e2f00c260513f3d4db42588fa8ba703c hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910560 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289774 name darwin64 @@ -1451,11 +1451,11 @@ creds github hash - 7ed994db5bafa9a7ad09a1b53da850a84715c65e + 01d08f13c7bc8d1b95b0330fa6833b7d8274e4d0 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910561 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289775 name linux64 @@ -1467,24 +1467,24 @@ creds github hash - 66824c02e0e5eabbfbe37bfb173360195f89697c + 6d00345c7d3471bc5f7c1218e014dd0f1a2c069b hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/178910562 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289778 name windows64 + copyright + Copyright (c) 2010, Linden Research, Inc. license internal license_file LICENSES/llphysicsextensions.txt - copyright - Copyright (c) 2010, Linden Research, Inc. version - 1.0.66e6919 + 1.0.11137145495 name llphysicsextensions_source @@ -2332,59 +2332,33 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors threejs - platforms - - darwin64 - - archive - - hash - cfed00d8ea7265c035c2d86a234b28efb0b23756 - hash_algorithm - sha1 - url - https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-b8f6746/threejs-0.132.2-darwin64-b8f6746.tar.zst - - name - darwin64 - - linux64 - - archive - - hash - 9de1295b157c9913c28be81ff933c73493ecc132 - hash_algorithm - sha1 - url - https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-b8f6746/threejs-0.132.2-linux64-b8f6746.tar.zst - - - windows64 - - archive - - hash - 4141710fccbd1ea2b3b53d00e189bdfa2ee9d441 - hash_algorithm - sha1 - url - https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-b8f6746/threejs-0.132.2-windows64-b8f6746.tar.zst - - name - windows64 - - + copyright + Copyright © 2010-2021 three.js authors license MIT license_file LICENSES/THREEJS_LICENSE.txt - copyright - Copyright © 2010-2021 three.js authors - version - 0.132.2 name threejs + platforms + + common + + archive + + hash + 982c0fa427458082ea9e3cb9603904210732b64e + hash_algorithm + sha1 + url + https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-5da28d9/threejs-0.132.2-common-8454371083.tar.zst + + name + common + + + version + 0.132.2 tinygltf diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/indra/edit-me-to-trigger-new-build.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h index 0046c5da7e..079bcd132a 100644 --- a/indra/llcharacter/llcharacter.h +++ b/indra/llcharacter/llcharacter.h @@ -119,6 +119,8 @@ public: virtual void addDebugText( const std::string& text ) = 0; + virtual std::string getDebugName() const { return getID().asString(); } + virtual const LLUUID& getID() const = 0; //------------------------------------------------------------------------- // End Interface diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index c09cf7abd2..9672a3262b 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -29,6 +29,7 @@ #include "llassettype.h" #include "lldictionary.h" #include "llmemory.h" +#include "llsd.h" #include "llsingleton.h" ///---------------------------------------------------------------------------- @@ -246,3 +247,19 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) } return false; } + +LLSD LLAssetType::getTypeNames() +{ + LLSD type_names; + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type) + { + const AssetEntry *entry = dict->lookup((LLAssetType::EType) type); + // skip llassettype_bad_lookup + if (entry) + { + type_names.append(entry->mTypeName); + } + } + return type_names; +} diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 547c3f4329..17177d81c3 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -165,6 +165,8 @@ public: static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer + static LLSD getTypeNames(); + static const std::string BADLOOKUP; protected: diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index a783e18e49..2e94651083 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -638,6 +638,14 @@ public: { getCPUIDInfo(); uint64_t frequency = getSysctlInt64("hw.cpufrequency"); + if (!frequency) + { + auto tbfrequency = getSysctlInt64("hw.tbfrequency"); + struct clockinfo clockrate; + auto clockrate_len = sizeof(clockrate); + if (!sysctlbyname("kern.clockrate", &clockrate, &clockrate_len, NULL, 0)) + frequency = tbfrequency * clockrate.hz; + } setInfo(eFrequency, (F64)frequency / (F64)1000000); } diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index c31030c5ea..497c0ad3eb 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -553,6 +553,45 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter } // namespace llsd +/***************************************************************************** +* LLSDParam> +*****************************************************************************/ +// Given an LLSD array, return a const std::vector&, where T is a type +// supported by LLSDParam. Bonus: if the LLSD value is actually a scalar, +// return a single-element vector containing the converted value. +template +class LLSDParam>: public LLSDParamBase +{ +public: + LLSDParam(const LLSD& array) + { + // treat undefined "array" as empty vector + if (array.isDefined()) + { + // what if it's a scalar? + if (! array.isArray()) + { + v.push_back(LLSDParam(array)); + } + else // really is an array + { + // reserve space for the array entries + v.reserve(array.size()); + for (const auto& item : llsd::inArray(array)) + { + v.push_back(LLSDParam(item)); + } + } + } + } + + operator const std::vector&() const { return v; } + +private: + std::vector v; +}; + + /***************************************************************************** * toArray(), toMap() *****************************************************************************/ diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index 0eb1891754..ac4f415f3e 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -55,7 +55,7 @@ namespace LL * ThreadPool listens for application shutdown messages on the "LLApp" * LLEventPump. Call close() to shut down this ThreadPool early. */ - virtual void close(); + void close(); std::string getName() const { return mName; } size_t getWidth() const { return mThreads.size(); } @@ -122,7 +122,7 @@ namespace LL size_t threads=1, size_t capacity=1024*1024, bool auto_shutdown = true): - ThreadPoolBase(name, threads, new queue_t(name, capacity), auto_shutdown) + ThreadPoolBase(name, threads, new queue_t(name, capacity, false), auto_shutdown) {} ~ThreadPoolUsing() override {} diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index ea4feec58c..7efaebd569 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -21,6 +21,7 @@ #include "llcoros.h" #include LLCOROS_MUTEX_HEADER #include "llerror.h" +#include "llevents.h" #include "llexception.h" #include "stringize.h" @@ -30,11 +31,38 @@ using Lock = LLCoros::LockType; /***************************************************************************** * WorkQueueBase *****************************************************************************/ -LL::WorkQueueBase::WorkQueueBase(const std::string& name): - super(makeName(name)) +LL::WorkQueueBase::WorkQueueBase(const std::string& name, bool auto_shutdown) + : super(makeName(name)) { - // TODO: register for "LLApp" events so we can implicitly close() on - // viewer shutdown. + if (auto_shutdown) + { + // Register for "LLApp" events so we can implicitly close() on viewer shutdown + std::string listener_name = "WorkQueue:" + getKey(); + LLEventPumps::instance().obtain("LLApp").listen( + listener_name, + [this](const LLSD& stat) + { + std::string status(stat["status"]); + if (status != "running") + { + // Viewer is shutting down, close this queue + LL_DEBUGS("WorkQueue") << getKey() << " closing on app shutdown" << LL_ENDL; + close(); + } + return false; + }); + + // Store the listener name so we can unregister in the destructor + mListenerName = listener_name; + } +} + +LL::WorkQueueBase::~WorkQueueBase() +{ + if (!mListenerName.empty() && !LLEventPumps::wasDeleted()) + { + LLEventPumps::instance().obtain("LLApp").stopListening(mListenerName); + } } void LL::WorkQueueBase::runUntilClose() @@ -220,8 +248,8 @@ void LL::WorkQueueBase::checkCoroutine(const std::string& method) /***************************************************************************** * WorkQueue *****************************************************************************/ -LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity): - super(name), +LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity, bool auto_shutdown): + super(name, auto_shutdown), mQueue(capacity) { } @@ -269,8 +297,8 @@ bool LL::WorkQueue::tryPop_(Work& work) /***************************************************************************** * WorkSchedule *****************************************************************************/ -LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity): - super(name), +LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity, bool auto_shutdown): + super(name, auto_shutdown), mQueue(capacity) { } diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 9d7bbfbf7a..735ad38a26 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -51,7 +51,9 @@ namespace LL * You may omit the WorkQueueBase name, in which case a unique name is * synthesized; for practical purposes that makes it anonymous. */ - WorkQueueBase(const std::string& name); + WorkQueueBase(const std::string& name, bool auto_shutdown); + + virtual ~WorkQueueBase(); /** * Since the point of WorkQueue is to pass work to some other worker @@ -197,6 +199,9 @@ namespace LL private: virtual Work pop_() = 0; virtual bool tryPop_(Work&) = 0; + + // Name used for the LLApp event listener (empty if not registered) + std::string mListenerName; }; /***************************************************************************** @@ -212,7 +217,7 @@ namespace LL * You may omit the WorkQueue name, in which case a unique name is * synthesized; for practical purposes that makes it anonymous. */ - WorkQueue(const std::string& name = std::string(), size_t capacity=1024); + WorkQueue(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true); /** * Since the point of WorkQueue is to pass work to some other worker @@ -282,7 +287,7 @@ namespace LL * You may omit the WorkSchedule name, in which case a unique name is * synthesized; for practical purposes that makes it anonymous. */ - WorkSchedule(const std::string& name = std::string(), size_t capacity=1024); + WorkSchedule(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true); /** * Since the point of WorkSchedule is to pass work to some other worker diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 6186e7a308..081a4d9bac 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -538,6 +538,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) long sslHostV(0L); long dnsCacheTimeout(-1L); long nobody(0L); + curl_off_t lastModified(0L); if (mReqOptions) { @@ -546,6 +547,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) sslHostV = mReqOptions->getSSLVerifyHost() ? 2L : 0L; dnsCacheTimeout = mReqOptions->getDNSCacheTimeout(); nobody = mReqOptions->getHeadersOnly() ? 1L : 0L; + lastModified = (curl_off_t)mReqOptions->getLastModified(); } check_curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, follow_redirect); @@ -554,6 +556,17 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) check_curl_easy_setopt(mCurlHandle, CURLOPT_NOBODY, nobody); + if (lastModified) + { + check_curl_easy_setopt(mCurlHandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); +#if (LIBCURL_VERSION_NUM >= 0x073B00) + // requires curl 7.59.0 + check_curl_easy_setopt(mCurlHandle, CURLOPT_TIMEVALUE_LARGE, lastModified); +#else + check_curl_easy_setopt(mCurlHandle, CURLOPT_TIMEVALUE, (long)lastModified); +#endif + } + // The Linksys WRT54G V5 router has an issue with frequent // DNS lookups from LAN machines. If they happen too often, // like for every HTTP request, the router gets annoyed after diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index d85f6039b1..5abd28e211 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -47,6 +47,7 @@ HttpOptions::HttpOptions() : mVerifyPeer(sDefaultVerifyPeer), mVerifyHost(false), mDNSCacheTimeout(-1L), + mLastModified(0), mNoBody(false) {} @@ -129,6 +130,11 @@ void HttpOptions::setHeadersOnly(bool nobody) } } +void HttpOptions::setLastModified(time_t lastModified) +{ + mLastModified = lastModified; +} + void HttpOptions::setDefaultSSLVerifyPeer(bool verify) { sDefaultVerifyPeer = verify; diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 56a28013cb..fdb277c66e 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -178,6 +178,13 @@ public: return mNoBody; } + // Default: 0 + void setLastModified(time_t lastModified); + time_t getLastModified() const + { + return mLastModified; + } + /// Sets default behavior for verifying that the name in the /// security certificate matches the name of the host contacted. /// Defaults false if not set, but should be set according to @@ -199,6 +206,7 @@ protected: bool mVerifyHost; int mDNSCacheTimeout; bool mNoBody; + time_t mLastModified; static bool sDefaultVerifyPeer; }; // end class HttpOptions diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp index 2a328675c2..c8f99380ea 100644 --- a/indra/llimage/llimagebmp.cpp +++ b/indra/llimage/llimagebmp.cpp @@ -558,6 +558,12 @@ bool LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time) LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL; } + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components); U8 magic[14]; diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp index 6b960f9077..c3fd0c5aa8 100644 --- a/indra/llimage/llimagedxt.cpp +++ b/indra/llimage/llimagedxt.cpp @@ -329,6 +329,12 @@ bool LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_ { llassert_always(raw_image); + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + S32 ncomponents = raw_image->getComponents(); EFileFormat format; switch (ncomponents) diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index 0e7ec365d4..effd33b410 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -491,6 +491,12 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time ) resetLastError(); + if (raw_image->isBufferInvalid()) + { + setLastError("Invalid input, no buffer"); + return false; + } + LLImageDataSharedLock lockIn(raw_image); LLImageDataLock lockOut(this); diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index c56e94aaa4..431f5aa001 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -897,6 +897,12 @@ bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible) { + if (raw_image.isBufferInvalid()) + { + base.setLastError("Invalid input, no buffer"); + return false; + } + JPEG2KEncode encode(comment_text, reversible); bool encoded = encode.encode(raw_image, base); if (!encoded) diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index 7e1be17ecc..670405e9b5 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -29,6 +29,7 @@ #include "llfoldertype.h" #include "lldictionary.h" #include "llmemory.h" +#include "llsd.h" #include "llsingleton.h" ///---------------------------------------------------------------------------- @@ -220,3 +221,21 @@ const std::string &LLFolderType::badLookup() static const std::string sBadLookup = "llfoldertype_bad_lookup"; return sBadLookup; } + +LLSD LLFolderType::getTypeNames() +{ + LLSD type_names; + for (S32 type = FT_TEXTURE; type < FT_COUNT; ++type) + { + if (lookupIsEnsembleType((LLFolderType::EType)type)) + continue; + + const FolderEntry* entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type); + // skip llfoldertype_bad_lookup + if (entry) + { + type_names.append(entry->mName); + } + } + return type_names; +} diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 46a1b92a96..dd12693f66 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -115,6 +115,8 @@ public: static const std::string& badLookup(); // error string when a lookup fails + static LLSD getTypeNames(); + protected: LLFolderType() {} ~LLFolderType() {} diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 075abf9536..3defad8f3b 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -46,6 +46,7 @@ static const std::string INV_ITEM_ID_LABEL("item_id"); static const std::string INV_FOLDER_ID_LABEL("cat_id"); static const std::string INV_PARENT_ID_LABEL("parent_id"); static const std::string INV_THUMBNAIL_LABEL("thumbnail"); +static const std::string INV_FAVORITE_LABEL("favorite"); static const std::string INV_THUMBNAIL_ID_LABEL("thumbnail_id"); static const std::string INV_ASSET_TYPE_LABEL("type"); static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type"); @@ -59,6 +60,7 @@ static const std::string INV_LINKED_ID_LABEL("linked_id"); static const std::string INV_SALE_INFO_LABEL("sale_info"); static const std::string INV_FLAGS_LABEL("flags"); static const std::string INV_CREATION_DATE_LABEL("created_at"); +static const std::string INV_TOGGLED_LABEL("toggled"); // key used by agent-inventory-service static const std::string INV_ASSET_TYPE_LABEL_WS("type_default"); @@ -82,14 +84,16 @@ LLInventoryObject::LLInventoryObject(const LLUUID& uuid, mParentUUID(parent_uuid), mType(type), mName(name), - mCreationDate(0) + mCreationDate(0), + mFavorite(false) { correctInventoryName(mName); } LLInventoryObject::LLInventoryObject() : mType(LLAssetType::AT_NONE), - mCreationDate(0) + mCreationDate(0), + mFavorite(false) { } @@ -104,6 +108,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other) mType = other->mType; mName = other->mName; mThumbnailUUID = other->mThumbnailUUID; + mFavorite = other->mFavorite; } const LLUUID& LLInventoryObject::getUUID() const @@ -121,6 +126,11 @@ const LLUUID& LLInventoryObject::getThumbnailUUID() const return mThumbnailUUID; } +bool LLInventoryObject::getIsFavorite() const +{ + return mFavorite; +} + const std::string& LLInventoryObject::getName() const { return mName; @@ -175,6 +185,11 @@ void LLInventoryObject::setThumbnailUUID(const LLUUID& thumbnail_uuid) mThumbnailUUID = thumbnail_uuid; } +void LLInventoryObject::setFavorite(bool favorite) +{ + mFavorite = favorite; +} + void LLInventoryObject::setType(LLAssetType::EType type) { mType = type; @@ -247,6 +262,23 @@ bool LLInventoryObject::importLegacyStream(std::istream& input_stream) { setThumbnailUUID(LLUUID::null); } + + if (metadata.has("favorite")) + { + const LLSD& favorite = metadata["favorite"]; + if (favorite.has("toggled")) + { + setFavorite(favorite["toggled"].asBoolean()); + } + else + { + setFavorite(false); + } + } + else + { + setFavorite(false); + } } else if(0 == strcmp("name", keyword)) { @@ -735,6 +767,23 @@ bool LLInventoryItem::importLegacyStream(std::istream& input_stream) { setThumbnailUUID(LLUUID::null); } + + if (metadata.has("favorite")) + { + const LLSD& favorite = metadata["favorite"]; + if (favorite.has("toggled")) + { + setFavorite(favorite["toggled"].asBoolean()); + } + else + { + setFavorite(false); + } + } + else + { + setFavorite(false); + } } else if(0 == strcmp("inv_type", keyword)) { @@ -879,7 +928,7 @@ bool LLInventoryItem::exportLegacyStream(std::ostream& output_stream, bool inclu LLSD LLInventoryItem::asLLSD() const { - LLSD sd = LLSD(); + LLSD sd; asLLSD(sd); return sd; } @@ -888,13 +937,18 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const { sd[INV_ITEM_ID_LABEL] = mUUID; sd[INV_PARENT_ID_LABEL] = mParentUUID; - sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions); + ll_fill_sd_from_permissions(sd[INV_PERMISSIONS_LABEL], mPermissions); if (mThumbnailUUID.notNull()) { sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); } + if (mFavorite) + { + sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite); + } + U32 mask = mPermissions.getMaskBase(); if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) || (mAssetUUID.isNull())) @@ -909,19 +963,22 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const cipher.encrypt(shadow_id.mData, UUID_BYTES); sd[INV_SHADOW_ID_LABEL] = shadow_id; } - sd[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); - sd[INV_INVENTORY_TYPE_LABEL] = mInventoryType; + sd[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType)); const std::string inv_type_str = LLInventoryType::lookup(mInventoryType); if(!inv_type_str.empty()) { sd[INV_INVENTORY_TYPE_LABEL] = inv_type_str; } + else + { + sd[INV_INVENTORY_TYPE_LABEL] = (LLSD::Integer)mInventoryType; + } //sd[INV_FLAGS_LABEL] = (S32)mFlags; sd[INV_FLAGS_LABEL] = ll_sd_from_U32(mFlags); - sd[INV_SALE_INFO_LABEL] = mSaleInfo.asLLSD(); + mSaleInfo.asLLSD(sd[INV_SALE_INFO_LABEL]); sd[INV_NAME_LABEL] = mName; sd[INV_DESC_LABEL] = mDescription; - sd[INV_CREATION_DATE_LABEL] = (S32) mCreationDate; + sd[INV_CREATION_DATE_LABEL] = (LLSD::Integer)mCreationDate; } bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) @@ -937,6 +994,8 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) // TODO - figure out if this should be moved into the noclobber fields above mThumbnailUUID.setNull(); + mFavorite = false; + mPermissions.init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); // iterate as map to avoid making unnecessary temp copies of everything LLSD::map_const_iterator i, end; @@ -982,9 +1041,20 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) continue; } + if (i->first == INV_FAVORITE_LABEL) + { + const LLSD& favorite_map = i->second; + const std::string w = INV_TOGGLED_LABEL; + if (favorite_map.has(w)) + { + mFavorite = favorite_map[w].asBoolean(); + } + continue; + } + if (i->first == INV_PERMISSIONS_LABEL) { - mPermissions = ll_permissions_from_sd(i->second); + mPermissions.importLLSD(i->second); continue; } @@ -1177,6 +1247,11 @@ LLSD LLInventoryCategory::asLLSD() const sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); } + if (mFavorite) + { + sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite); + } + return sd; } @@ -1188,11 +1263,17 @@ LLSD LLInventoryCategory::asAISCreateCatLLSD() const S8 type = static_cast(mPreferredType); sd[INV_ASSET_TYPE_LABEL_WS] = type; sd[INV_NAME_LABEL] = mName; + if (mThumbnailUUID.notNull()) { sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); } + if (mFavorite) + { + sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite); + } + return sd; } @@ -1240,6 +1321,17 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd) mThumbnailUUID = sd[w]; } } + mFavorite = false; + w = INV_FAVORITE_LABEL; + if (sd.has(w)) + { + const LLSD& favorite_map = sd[w]; + w = INV_TOGGLED_LABEL; + if (favorite_map.has(w)) + { + mFavorite = favorite_map[w].asBoolean(); + } + } w = INV_ASSET_TYPE_LABEL; if (sd.has(w)) { @@ -1362,6 +1454,23 @@ bool LLInventoryCategory::importLegacyStream(std::istream& input_stream) { setThumbnailUUID(LLUUID::null); } + + if (metadata.has("favorite")) + { + const LLSD& favorite = metadata["favorite"]; + if (favorite.has("toggled")) + { + setFavorite(favorite["toggled"].asBoolean()); + } + else + { + setFavorite(false); + } + } + else + { + setFavorite(false); + } } else { @@ -1396,12 +1505,11 @@ bool LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, bool) return true; } -LLSD LLInventoryCategory::exportLLSD() const +void LLInventoryCategory::exportLLSD(LLSD& cat_data) const { - LLSD cat_data; cat_data[INV_FOLDER_ID_LABEL] = mUUID; cat_data[INV_PARENT_ID_LABEL] = mParentUUID; - cat_data[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); + cat_data[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType)); cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType); cat_data[INV_NAME_LABEL] = mName; @@ -1409,49 +1517,76 @@ LLSD LLInventoryCategory::exportLLSD() const { cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); } - - return cat_data; + if (mFavorite) + { + cat_data[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite); + } } -bool LLInventoryCategory::importLLSD(const LLSD& cat_data) +bool LLInventoryCategory::importLLSDMap(const LLSD& cat_data) { - if (cat_data.has(INV_FOLDER_ID_LABEL)) + LLSD::map_const_iterator i, end; + end = cat_data.endMap(); + for ( i = cat_data.beginMap(); i != end; ++i) { - setUUID(cat_data[INV_FOLDER_ID_LABEL].asUUID()); + importLLSD(i->first, i->second); } - if (cat_data.has(INV_PARENT_ID_LABEL)) - { - setParent(cat_data[INV_PARENT_ID_LABEL].asUUID()); - } - if (cat_data.has(INV_ASSET_TYPE_LABEL)) - { - setType(LLAssetType::lookup(cat_data[INV_ASSET_TYPE_LABEL].asString())); - } - if (cat_data.has(INV_PREFERRED_TYPE_LABEL)) - { - setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString())); - } - if (cat_data.has(INV_THUMBNAIL_LABEL)) - { - LLUUID thumbnail_uuid; - const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL]; - if (thumbnail_data.has(INV_ASSET_ID_LABEL)) - { - thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID(); - } - setThumbnailUUID(thumbnail_uuid); - } - if (cat_data.has(INV_NAME_LABEL)) - { - mName = cat_data[INV_NAME_LABEL].asString(); - LLStringUtil::replaceNonstandardASCII(mName, ' '); - LLStringUtil::replaceChar(mName, '|', ' '); - } - return true; } + +bool LLInventoryCategory::importLLSD(const std::string& label, const LLSD& value) +{ + if (label == INV_FOLDER_ID_LABEL) + { + setUUID(value.asUUID()); + return true; + } + else if (label == INV_PARENT_ID_LABEL) + { + setParent(value.asUUID()); + return true; + } + else if (label == INV_ASSET_TYPE_LABEL) + { + setType(LLAssetType::lookup(value.asString())); + return true; + } + else if (label == INV_PREFERRED_TYPE_LABEL) + { + setPreferredType(LLFolderType::lookup(value.asString())); + return true; + } + else if (label == INV_THUMBNAIL_LABEL) + { + LLUUID thumbnail_uuid; + if (value.has(INV_ASSET_ID_LABEL)) + { + thumbnail_uuid = value[INV_ASSET_ID_LABEL].asUUID(); + } + setThumbnailUUID(thumbnail_uuid); + return true; + } + if (label == INV_FAVORITE_LABEL) + { + bool favorite = false; + if (value.has(INV_TOGGLED_LABEL)) + { + favorite = value[INV_TOGGLED_LABEL].asBoolean(); + } + setFavorite(favorite); + } + else if (label == INV_NAME_LABEL) + { + mName = value.asString(); + LLStringUtil::replaceNonstandardASCII(mName, ' '); + LLStringUtil::replaceChar(mName, '|', ' '); + return true; + } + return false; +} + ///---------------------------------------------------------------------------- -/// Local function definitions +/// Local function definitions for testing purposes ///---------------------------------------------------------------------------- LLSD ll_create_sd_from_inventory_item(LLPointer item) diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index e63f2deba7..181c46226c 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -71,6 +71,7 @@ public: virtual const LLUUID& getLinkedUUID() const; // inventoryID that this item points to, else this item's inventoryID const LLUUID& getParentUUID() const; virtual const LLUUID& getThumbnailUUID() const; + virtual bool getIsFavorite() const; virtual const std::string& getName() const; virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items @@ -86,6 +87,7 @@ public: virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); virtual void setThumbnailUUID(const LLUUID& thumbnail_uuid); + virtual void setFavorite(bool favorite); void setType(LLAssetType::EType type); virtual void setCreationDate(time_t creation_date_utc); // only stored for items @@ -111,6 +113,7 @@ protected: LLUUID mUUID; LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL. LLUUID mThumbnailUUID; + bool mFavorite; LLAssetType::EType mType; std::string mName; time_t mCreationDate; // seconds from 1/1/1970, UTC @@ -270,8 +273,9 @@ public: virtual bool importLegacyStream(std::istream& input_stream); virtual bool exportLegacyStream(std::ostream& output_stream, bool include_asset_key = true) const; - LLSD exportLLSD() const; - bool importLLSD(const LLSD& cat_data); + virtual void exportLLSD(LLSD& sd) const; + bool importLLSDMap(const LLSD& cat_data); + virtual bool importLLSD(const std::string& label, const LLSD& value); //-------------------------------------------------------------------- // Member Variables //-------------------------------------------------------------------- @@ -285,6 +289,7 @@ protected: // // These functions convert between structured data and an inventory // item, appropriate for serialization. +// Not up to date (no favorites, nor thumbnails), for testing purposes //----------------------------------------------------------------------------- LLSD ll_create_sd_from_inventory_item(LLPointer item); LLSD ll_create_sd_from_inventory_category(LLPointer cat); diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index c8963881df..ebf7445c65 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -704,6 +704,79 @@ bool LLPermissions::exportLegacyStream(std::ostream& output_stream) const return true; } +static const std::string PERM_CREATOR_ID_LABEL("creator_id"); +static const std::string PERM_OWNER_ID_LABEL("owner_id"); +static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id"); +static const std::string PERM_GROUP_ID_LABEL("group_id"); +static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group"); +static const std::string PERM_BASE_MASK_LABEL("base_mask"); +static const std::string PERM_OWNER_MASK_LABEL("owner_mask"); +static const std::string PERM_GROUP_MASK_LABEL("group_mask"); +static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask"); +static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); + +void LLPermissions::importLLSD(const LLSD& sd_perm) +{ + LLSD::map_const_iterator i, end; + end = sd_perm.endMap(); + for (i = sd_perm.beginMap(); i != end; ++i) + { + const std::string& label = i->first; + if (label == PERM_CREATOR_ID_LABEL) + { + mCreator = i->second.asUUID(); + continue; + } + if (label == PERM_OWNER_ID_LABEL) + { + mOwner = i->second.asUUID(); + continue; + } + if (label == PERM_LAST_OWNER_ID_LABEL) + { + mLastOwner = i->second.asUUID(); + continue; + } + if (label == PERM_GROUP_ID_LABEL) + { + mGroup = i->second.asUUID(); + continue; + } + if (label == PERM_BASE_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskBase = mask; + continue; + } + if (label == PERM_OWNER_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskOwner = mask; + continue; + } + if (label == PERM_EVERYONE_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskEveryone = mask; + continue; + } + if (label == PERM_GROUP_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskGroup = mask; + continue; + } + if (label == PERM_NEXT_OWNER_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskNextOwner = mask; + continue; + } + } + + fix(); +} + bool LLPermissions::operator==(const LLPermissions &rhs) const { return @@ -998,55 +1071,30 @@ std::string mask_to_string(U32 mask) ///---------------------------------------------------------------------------- /// exported functions ///---------------------------------------------------------------------------- -static const std::string PERM_CREATOR_ID_LABEL("creator_id"); -static const std::string PERM_OWNER_ID_LABEL("owner_id"); -static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id"); -static const std::string PERM_GROUP_ID_LABEL("group_id"); -static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group"); -static const std::string PERM_BASE_MASK_LABEL("base_mask"); -static const std::string PERM_OWNER_MASK_LABEL("owner_mask"); -static const std::string PERM_GROUP_MASK_LABEL("group_mask"); -static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask"); -static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); LLSD ll_create_sd_from_permissions(const LLPermissions& perm) { LLSD rv; + ll_fill_sd_from_permissions(rv, perm); + return rv; +} +void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm) +{ rv[PERM_CREATOR_ID_LABEL] = perm.getCreator(); rv[PERM_OWNER_ID_LABEL] = perm.getOwner(); rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner(); rv[PERM_GROUP_ID_LABEL] = perm.getGroup(); rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned(); - rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase(); - rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner(); - rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup(); - rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone(); - rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner(); - return rv; + rv[PERM_BASE_MASK_LABEL] = (LLSD::Integer)perm.getMaskBase(); + rv[PERM_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskOwner(); + rv[PERM_GROUP_MASK_LABEL] = (LLSD::Integer)perm.getMaskGroup(); + rv[PERM_EVERYONE_MASK_LABEL] = (LLSD::Integer)perm.getMaskEveryone(); + rv[PERM_NEXT_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskNextOwner(); } LLPermissions ll_permissions_from_sd(const LLSD& sd_perm) { LLPermissions rv; - rv.init( - sd_perm[PERM_CREATOR_ID_LABEL].asUUID(), - sd_perm[PERM_OWNER_ID_LABEL].asUUID(), - sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(), - sd_perm[PERM_GROUP_ID_LABEL].asUUID()); - - // We do a cast to U32 here since LLSD does not attempt to - // represent unsigned ints. - PermissionMask mask; - mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger()); - rv.setMaskBase(mask); - mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger()); - rv.setMaskOwner(mask); - mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger()); - rv.setMaskEveryone(mask); - mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger()); - rv.setMaskGroup(mask); - mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger()); - rv.setMaskNext(mask); - rv.fix(); + rv.importLLSD(sd_perm); return rv; } diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index a68abcfa34..82cdc03727 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -299,6 +299,8 @@ public: bool importLegacyStream(std::istream& input_stream); bool exportLegacyStream(std::ostream& output_stream) const; + void importLLSD(const LLSD& sd_perm); + bool operator==(const LLPermissions &rhs) const; bool operator!=(const LLPermissions &rhs) const; @@ -435,6 +437,7 @@ protected: // like 'creator_id', 'owner_id', etc, with the value copied from the // permission object. LLSD ll_create_sd_from_permissions(const LLPermissions& perm); +void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm); LLPermissions ll_permissions_from_sd(const LLSD& sd_perm); #endif diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 35bbc1dbb1..b4d64bb4fb 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -90,15 +90,20 @@ bool LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const LLSD LLSaleInfo::asLLSD() const { LLSD sd; + asLLSD(sd); + return sd; +} + +void LLSaleInfo::asLLSD(LLSD& sd) const +{ const char* type = lookup(mSaleType); if (!type) { LL_WARNS_ONCE() << "Unknown sale type: " << mSaleType << LL_ENDL; type = lookup(LLSaleInfo::FS_NOT); } - sd["sale_type"] = type; + sd["sale_type"] = std::string(type); sd["sale_price"] = mSalePrice; - return sd; } bool LLSaleInfo::fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask) diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h index 44eb841641..7186e8ab49 100644 --- a/indra/llinventory/llsaleinfo.h +++ b/indra/llinventory/llsaleinfo.h @@ -86,6 +86,7 @@ public: bool exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; + void asLLSD(LLSD &sd) const; operator LLSD() const { return asLLSD(); } bool fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask); bool importLegacyStream(std::istream& input_stream, bool& has_perm_mask, U32& perm_mask); diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index 9779cb8fbc..f11a4c3bf7 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -39,6 +39,34 @@ #pragma warning(disable: 4702) #endif +void set_random_inventory_metadata(LLInventoryObject* obj) +{ + S32 extra = rand() % 4; + switch (extra) + { + case 0: + { + LLUUID thumbnail_id; + thumbnail_id.generate(); + obj->setThumbnailUUID(thumbnail_id); + break; + } + case 1: + obj->setFavorite(true); + break; + case 2: + { + LLUUID thumbnail_id; + thumbnail_id.generate(); + obj->setThumbnailUUID(thumbnail_id); + obj->setFavorite(true); + break; + } + default: + break; + } +} + LLPointer create_random_inventory_item() { LLUUID item_id; @@ -75,6 +103,7 @@ LLPointer create_random_inventory_item() sale_info, flags, creation); + set_random_inventory_metadata(item); return item; } @@ -90,6 +119,7 @@ LLPointer create_random_inventory_cat() parent_id, LLFolderType::FT_NONE, std::string("Sample category")); + set_random_inventory_metadata(cat); return cat; } @@ -290,6 +320,7 @@ namespace tut src->setCreationDate(new_creation); // test a save/load cycle to LLSD and back again + // Note: ll_create_sd_from_inventory_item does not support metadata LLSD sd = ll_create_sd_from_inventory_item(src); LLPointer dst = new LLInventoryItem; bool successful_parse = dst->fromLLSD(sd); @@ -329,7 +360,9 @@ namespace tut } LLPointer src1 = create_random_inventory_item(); - fileXML << LLSDOStreamer(src1->asLLSD()) << std::endl; + LLSD sd; + src1->asLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; fileXML.close(); @@ -364,13 +397,13 @@ namespace tut ensure_equals("8.name::getName() failed", src1->getName(), src2->getName()); ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription()); ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); - + ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); + ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); } template<> template<> void inventory_object::test<8>() { - LLPointer src1 = create_random_inventory_item(); std::ostringstream ostream; @@ -390,8 +423,8 @@ namespace tut ensure_equals("8.name::getName() failed", src1->getName(), src2->getName()); ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription()); ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); - - + ensure_equals("11.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); + ensure_equals("12.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // not supposed to carry over } template<> template<> @@ -421,6 +454,8 @@ namespace tut ensure_equals("10.name::getName() failed", src1->getName(), src2->getName()); ensure_equals("11.description::getDescription() failed", src1->getDescription(), src2->getDescription()); ensure_equals("12.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate()); + ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); + ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); } //******class LLInventoryCategory*******// @@ -458,7 +493,9 @@ namespace tut } LLPointer src1 = create_random_inventory_cat(); - fileXML << LLSDOStreamer(src1->exportLLSD()) << std::endl; + LLSD sd; + src1->exportLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; fileXML.close(); llifstream file(filename.c_str()); @@ -481,13 +518,15 @@ namespace tut file.close(); LLPointer src2 = new LLInventoryCategory(); - src2->importLLSD(s_item); + src2->importLLSDMap(s_item); ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType()); ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); + ensure_equals("6.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); + ensure_equals("7.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite()); } template<> template<> @@ -507,6 +546,7 @@ namespace tut ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType()); ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); - + ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID()); + ensure_equals("14.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // currently not supposed to carry over } } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 0d1f2b3006..b824fd8385 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -662,6 +662,12 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co bool vflip = true; bool hflip = false; + if (raw_image.isBufferInvalid()) + { + base.setLastError("Invalid input, no buffer"); + return false; + } + try { // Set up input image files diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp index 15c9f6ff3f..b42541c7c0 100644 --- a/indra/llmath/llcoordframe.cpp +++ b/indra/llmath/llcoordframe.cpp @@ -328,28 +328,30 @@ void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix) } +// Rotate 2 normalized orthogonal vectors in direction from `source` to `target` +static void rotate2(LLVector3& source, LLVector3& target, F32 angle) +{ + F32 sx = source[VX], sy = source[VY], sz = source[VZ]; + F32 tx = target[VX], ty = target[VY], tz = target[VZ]; + F32 c = cosf(angle), s = sinf(angle); + + source.set(sx * c + tx * s, sy * c + ty * s, sz * c + tz * s); + target.set(tx * c - sx * s, ty * c - sy * s, tz * c - sz * s); +} + void LLCoordFrame::roll(F32 angle) { - LLQuaternion q(angle, mXAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); - CHECK_FINITE_OBJ(); + rotate2(mYAxis, mZAxis, angle); } void LLCoordFrame::pitch(F32 angle) { - LLQuaternion q(angle, mYAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); - CHECK_FINITE_OBJ(); + rotate2(mZAxis, mXAxis, angle); } void LLCoordFrame::yaw(F32 angle) { - LLQuaternion q(angle, mZAxis); - LLMatrix3 rotation_matrix(q); - rotate(rotation_matrix); - CHECK_FINITE_OBJ(); + rotate2(mXAxis, mYAxis, angle); } // get*() routines diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 7f51de7820..891f0ffc4c 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -65,7 +65,7 @@ constexpr F32 DEG_TO_RAD = 0.017453292519943295769236907684886f; constexpr F32 RAD_TO_DEG = 57.295779513082320876798154814105f; constexpr F32 F_APPROXIMATELY_ZERO = 0.00001f; constexpr F32 F_LN10 = 2.3025850929940456840179914546844f; -constexpr F32 OO_LN10 = 0.43429448190325182765112891891661; +constexpr F32 OO_LN10 = 0.43429448190325182765112891891661f; constexpr F32 F_LN2 = 0.69314718056f; constexpr F32 OO_LN2 = 1.4426950408889634073599246810019f; diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index 1ab3a73d79..3a6748bdb3 100644 --- a/indra/llmath/llquaternion.cpp +++ b/indra/llmath/llquaternion.cpp @@ -57,7 +57,7 @@ LLQuaternion::LLQuaternion(const LLMatrix3 &mat) LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec) { - F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + F32 mag = vec.length(); if (mag > FP_MAG_THRESHOLD) { angle *= 0.5; @@ -76,7 +76,7 @@ LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec) LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec) { - F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]); + F32 mag = vec.length(); if (mag > FP_MAG_THRESHOLD) { angle *= 0.5; diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 1e7dfd18f2..7c60253618 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5711,7 +5711,15 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) S32 vert_count = 0; if (!data.p.empty()) { - vert_count = static_cast(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count)); + try + { + vert_count = static_cast(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count)); + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Failed to allocate memory for VertexRemap: " << (S32)data.p.size() << LL_ENDL; + } } if (vert_count < 65535 && vert_count != 0) diff --git a/indra/llmath/v2math.cpp b/indra/llmath/v2math.cpp index 59e6d947ca..175f08df88 100644 --- a/indra/llmath/v2math.cpp +++ b/indra/llmath/v2math.cpp @@ -66,6 +66,13 @@ F32 angle_between(const LLVector2& a, const LLVector2& b) return angle; } +F32 signed_angle_between(const LLVector2& a, const LLVector2& b) +{ + F32 angle = angle_between(a, b); + F32 rhombus_square = a[VX] * b[VY] - b[VX] * a[VY]; + return rhombus_square < 0 ? -angle : angle; +} + bool are_parallel(const LLVector2& a, const LLVector2& b, F32 epsilon) { LLVector2 an = a; diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h index 18ad02a411..6b9d37535b 100644 --- a/indra/llmath/v2math.h +++ b/indra/llmath/v2math.h @@ -107,13 +107,14 @@ class LLVector2 friend LLVector2 operator-(const LLVector2 &a); // Return vector -a - friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a + friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a }; // Non-member functions F32 angle_between(const LLVector2& a, const LLVector2& b); // Returns angle (radians) between a and b +F32 signed_angle_between(const LLVector2& a, const LLVector2& b); // Returns signed angle (radians) between a and b bool are_parallel(const LLVector2& a, const LLVector2& b, F32 epsilon = F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel F32 dist_vec(const LLVector2& a, const LLVector2& b); // Returns distance between a and b F32 dist_vec_squared(const LLVector2& a, const LLVector2& b);// Returns distance squared between a and b @@ -124,26 +125,22 @@ LLVector2 lerp(const LLVector2& a, const LLVector2& b, F32 u); // Returns a vect inline LLVector2::LLVector2() { - mV[VX] = 0.f; - mV[VY] = 0.f; + clear(); } inline LLVector2::LLVector2(F32 x, F32 y) { - mV[VX] = x; - mV[VY] = y; + set(x, y); } inline LLVector2::LLVector2(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; + set(vec); } inline LLVector2::LLVector2(const LLVector3 &vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; + set(vec.mV); } inline LLVector2::LLVector2(const LLSD &sd) @@ -155,28 +152,24 @@ inline LLVector2::LLVector2(const LLSD &sd) inline void LLVector2::clear() { - mV[VX] = 0.f; - mV[VY] = 0.f; + mV[VX] = mV[VY] = 0.f; } inline void LLVector2::setZero() { - mV[VX] = 0.f; - mV[VY] = 0.f; + clear(); } // deprecated inline void LLVector2::clearVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; + clear(); } // deprecated inline void LLVector2::zeroVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; + clear(); } inline void LLVector2::set(F32 x, F32 y) @@ -187,36 +180,31 @@ inline void LLVector2::set(F32 x, F32 y) inline void LLVector2::set(const LLVector2 &vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; + set(vec.mV); } inline void LLVector2::set(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; + set(vec[VX], vec[VY]); } // deprecated inline void LLVector2::setVec(F32 x, F32 y) { - mV[VX] = x; - mV[VY] = y; + set(x, y); } // deprecated inline void LLVector2::setVec(const LLVector2 &vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; + set(vec); } // deprecated inline void LLVector2::setVec(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; + set(vec); } @@ -224,7 +212,7 @@ inline void LLVector2::setVec(const F32 *vec) inline F32 LLVector2::length() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]); + return sqrt(lengthSquared()); } inline F32 LLVector2::lengthSquared() const @@ -234,61 +222,42 @@ inline F32 LLVector2::lengthSquared() const inline F32 LLVector2::normalize() { - F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]); - F32 oomag; + F32 mag = length(); if (mag > FP_MAG_THRESHOLD) { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; + *this /= mag; } else { - mV[VX] = 0.f; - mV[VY] = 0.f; + clear(); mag = 0; } - return (mag); + return mag; } // checker inline bool LLVector2::isFinite() const { - return (llfinite(mV[VX]) && llfinite(mV[VY])); + return llfinite(mV[VX]) && llfinite(mV[VY]); } // deprecated inline F32 LLVector2::magVec() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]); + return length(); } // deprecated inline F32 LLVector2::magVecSquared() const { - return mV[VX]*mV[VX] + mV[VY]*mV[VY]; + return lengthSquared(); } // deprecated inline F32 LLVector2::normVec() { - F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]); - F32 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; - } - else - { - mV[VX] = 0.f; - mV[VY] = 0.f; - mag = 0; - } - return (mag); + return normalize(); } inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec) @@ -301,11 +270,7 @@ inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec) inline bool LLVector2::isNull() const { - if (F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY]) - { - return true; - } - return false; + return F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY]; } @@ -405,10 +370,7 @@ inline const LLVector2& operator*=(LLVector2& a, F32 k) inline const LLVector2& operator/=(LLVector2& a, F32 k) { - F32 t = 1.f / k; - a.mV[VX] *= t; - a.mV[VY] *= t; - return a; + return a *= 1.f / k; } inline LLVector2 operator-(const LLVector2& a) diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h index 551c7df6c9..098ca5218c 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -112,24 +112,24 @@ class LLVector3 const LLVector3& setVec(const LLVector4 &vec); // deprecated const LLVector3& setVec(const LLVector3d &vec); // deprecated - F32 length() const; // Returns magnitude of LLVector3 - F32 lengthSquared() const; // Returns magnitude squared of LLVector3 - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated + F32 length() const; // Returns magnitude of LLVector3 + F32 lengthSquared() const; // Returns magnitude squared of LLVector3 + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated - inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3 - inline F32 normVec(); // deprecated + inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3 + inline F32 normVec(); // deprecated - inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max + inline bool inRange(F32 min, F32 max) const; // Returns true if all values of the vector are between min and max - const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians - const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians - const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat - const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q - const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v) + const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians + const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians + const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat + const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q + const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v) - const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec - LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec + const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec + LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec bool isNull() const; // Returns true if vector has a _very_small_ length bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; } @@ -183,23 +183,17 @@ bool box_valid_and_non_zero(const LLVector3* box); inline LLVector3::LLVector3() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + clear(); } inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; + set(x, y, z); } inline LLVector3::LLVector3(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; + set(vec); } inline LLVector3::LLVector3(const glm::vec3& vec) @@ -230,7 +224,7 @@ inline LLVector3::LLVector3(const LLVector3 ©) // checker inline bool LLVector3::isFinite() const { - return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ])); + return llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]); } @@ -238,30 +232,22 @@ inline bool LLVector3::isFinite() const inline void LLVector3::clear() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + set(0.f, 0.f, 0.f); } inline void LLVector3::setZero() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + clear(); } inline void LLVector3::clearVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + clear(); } inline void LLVector3::zeroVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + clear(); } inline void LLVector3::set(F32 x, F32 y, F32 z) @@ -273,16 +259,12 @@ inline void LLVector3::set(F32 x, F32 y, F32 z) inline void LLVector3::set(const LLVector3& vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; + set(vec.mV[VX], vec.mV[VY], vec.mV[VZ]); } inline void LLVector3::set(const F32* vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; + set(vec[VX], vec[VY], vec[VZ]); } inline void LLVector3::set(const glm::vec4& vec) @@ -302,95 +284,66 @@ inline void LLVector3::set(const glm::vec3& vec) // deprecated inline void LLVector3::setVec(F32 x, F32 y, F32 z) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; + set(x, y, z); } // deprecated inline void LLVector3::setVec(const LLVector3& vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; + set(vec); } // deprecated inline void LLVector3::setVec(const F32* vec) { - mV[VX] = vec[0]; - mV[VY] = vec[1]; - mV[VZ] = vec[2]; + set(vec); } inline F32 LLVector3::normalize() { F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); - F32 oomag; if (mag > FP_MAG_THRESHOLD) { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; - mV[VZ] *= oomag; + *this /= mag; } else { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; + clear(); mag = 0; } - return (mag); + return mag; } // deprecated inline F32 LLVector3::normVec() { - F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); - F32 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; - mV[VZ] *= oomag; - } - else - { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mag = 0; - } - return (mag); + return normalize(); } // LLVector3 Magnitude and Normalization Functions -inline F32 LLVector3::length() const +inline F32 LLVector3::length() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + return sqrt(lengthSquared()); } -inline F32 LLVector3::lengthSquared() const +inline F32 LLVector3::lengthSquared() const { return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; } -inline F32 LLVector3::magVec() const +inline F32 LLVector3::magVec() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + return length(); } -inline F32 LLVector3::magVecSquared() const +inline F32 LLVector3::magVecSquared() const { - return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; + return lengthSquared(); } -inline bool LLVector3::inRange( F32 min, F32 max ) const +inline bool LLVector3::inRange(F32 min, F32 max) const { return mV[VX] >= min && mV[VX] <= max && mV[VY] >= min && mV[VY] <= max && @@ -416,7 +369,7 @@ inline F32 operator*(const LLVector3& a, const LLVector3& b) inline LLVector3 operator%(const LLVector3& a, const LLVector3& b) { - return LLVector3( a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY] ); + return LLVector3(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]); } inline LLVector3 operator/(const LLVector3& a, F32 k) @@ -476,7 +429,7 @@ inline const LLVector3& operator-=(LLVector3& a, const LLVector3& b) inline const LLVector3& operator%=(LLVector3& a, const LLVector3& b) { - LLVector3 ret( a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]); + LLVector3 ret(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]); a = ret; return a; } @@ -499,9 +452,7 @@ inline const LLVector3& operator*=(LLVector3& a, const LLVector3& b) inline const LLVector3& operator/=(LLVector3& a, F32 k) { - a.mV[VX] /= k; - a.mV[VY] /= k; - a.mV[VZ] /= k; + a *= 1.f / k; return a; } @@ -526,7 +477,7 @@ inline F32 dist_vec(const LLVector3& a, const LLVector3& b) F32 x = a.mV[VX] - b.mV[VX]; F32 y = a.mV[VY] - b.mV[VY]; F32 z = a.mV[VZ] - b.mV[VZ]; - return sqrt( x*x + y*y + z*z ); + return sqrt(x*x + y*y + z*z); } inline F32 dist_vec_squared(const LLVector3& a, const LLVector3& b) @@ -551,10 +502,7 @@ inline LLVector3 projected_vec(const LLVector3& a, const LLVector3& b) { return ((a * b) / bb) * b; } - else - { - return b.zero; - } + return b.zero; } inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b) @@ -591,11 +539,7 @@ inline LLVector3 lerp(const LLVector3& a, const LLVector3& b, F32 u) inline bool LLVector3::isNull() const { - if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] ) - { - return true; - } - return false; + return F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; } inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos) @@ -636,7 +580,7 @@ inline F32 angle_between(const LLVector3& a, const LLVector3& b) ab = 0.0f; // get rid of negative zero } LLVector3 c = a % b; // crossproduct - return atan2f(sqrtf(c * c), ab); // return the angle + return atan2f(c.length(), ab); // return the angle } inline bool are_parallel(const LLVector3& a, const LLVector3& b, F32 epsilon) @@ -646,7 +590,7 @@ inline bool are_parallel(const LLVector3& a, const LLVector3& b, F32 epsilon) an.normalize(); bn.normalize(); F32 dot = an * bn; - if ( (1.0f - fabs(dot)) < epsilon) + if (1.0f - fabs(dot) < epsilon) { return true; } diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h index 37492e7f98..1cbd0d9a78 100644 --- a/indra/llmath/v4math.h +++ b/indra/llmath/v4math.h @@ -107,9 +107,9 @@ public: F32 lengthSquared() const; // Returns magnitude squared of LLVector4 F32 normalize(); // Normalizes and returns the magnitude of LLVector4 - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated - F32 normVec(); // deprecated + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated // Sets all values to absolute value of their original values // Returns true if data changed @@ -118,8 +118,8 @@ public: bool isExactlyClear() const { return (mV[VW] == 1.0f) && !mV[VX] && !mV[VY] && !mV[VZ]; } bool isExactlyZero() const { return !mV[VW] && !mV[VX] && !mV[VY] && !mV[VZ]; } - const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat - const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q + const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat + const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q const LLVector4& scaleVec(const LLVector4& vec); // Scales component-wise by vec @@ -159,34 +159,22 @@ LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u); // Returns a vect inline LLVector4::LLVector4(void) { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mV[VW] = 1.f; + clear(); } inline LLVector4::LLVector4(F32 x, F32 y, F32 z) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; - mV[VW] = 1.f; + set(x, y, z, 1.f); } inline LLVector4::LLVector4(F32 x, F32 y, F32 z, F32 w) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; - mV[VW] = w; + set(x, y, z, w); } inline LLVector4::LLVector4(const F32 *vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; - mV[VW] = vec[VW]; + set(vec); } inline LLVector4::LLVector4(const F64 *vec) @@ -215,18 +203,12 @@ inline LLVector4::LLVector4(const LLVector2 &vec, F32 z, F32 w) inline LLVector4::LLVector4(const LLVector3 &vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; - mV[VW] = 1.f; + set(vec, 1.f); } inline LLVector4::LLVector4(const LLVector3 &vec, F32 w) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; - mV[VW] = w; + set(vec, w); } inline LLVector4::LLVector4(const LLSD &sd) @@ -252,43 +234,31 @@ inline LLVector4::LLVector4(const glm::vec4& vec) inline bool LLVector4::isFinite() const { - return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW])); + return llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]); } // Clear and Assignment Functions inline void LLVector4::clear() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mV[VW] = 1.f; + set(0.f, 0.f, 0.f, 1.f); } // deprecated inline void LLVector4::clearVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mV[VW] = 1.f; + clear(); } // deprecated inline void LLVector4::zeroVec() { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mV[VW] = 0.f; + set(0.f, 0.f, 0.f, 0.f); } inline void LLVector4::set(F32 x, F32 y, F32 z) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; - mV[VW] = 1.f; + set(x, y, z, 1.f); } inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w) @@ -301,10 +271,7 @@ inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w) inline void LLVector4::set(const LLVector4& vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; - mV[VW] = vec.mV[VW]; + set(vec.mV); } inline void LLVector4::set(const LLVector3& vec, F32 w) @@ -322,7 +289,6 @@ inline void LLVector4::set(const F32* vec) mV[VZ] = vec[VZ]; mV[VW] = vec[VW]; } - inline void LLVector4::set(const glm::vec4& vec) { mV[VX] = vec.x; @@ -342,68 +308,53 @@ inline void LLVector4::set(const glm::vec3& vec, F32 w) // deprecated inline void LLVector4::setVec(F32 x, F32 y, F32 z) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; - mV[VW] = 1.f; + set(x, y, z); } // deprecated inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w) { - mV[VX] = x; - mV[VY] = y; - mV[VZ] = z; - mV[VW] = w; + set(x, y, z, w); } // deprecated inline void LLVector4::setVec(const LLVector4& vec) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; - mV[VW] = vec.mV[VW]; + set(vec); } // deprecated inline void LLVector4::setVec(const LLVector3& vec, F32 w) { - mV[VX] = vec.mV[VX]; - mV[VY] = vec.mV[VY]; - mV[VZ] = vec.mV[VZ]; - mV[VW] = w; + set(vec, w); } // deprecated inline void LLVector4::setVec(const F32* vec) { - mV[VX] = vec[VX]; - mV[VY] = vec[VY]; - mV[VZ] = vec[VZ]; - mV[VW] = vec[VW]; + set(vec); } // LLVector4 Magnitude and Normalization Functions -inline F32 LLVector4::length() const +inline F32 LLVector4::length() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + return sqrt(lengthSquared()); } -inline F32 LLVector4::lengthSquared() const +inline F32 LLVector4::lengthSquared() const { return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; } -inline F32 LLVector4::magVec() const +inline F32 LLVector4::magVec() const { - return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); + return length(); } -inline F32 LLVector4::magVecSquared() const +inline F32 LLVector4::magVecSquared() const { - return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]; + return lengthSquared(); } // LLVector4 Operators @@ -422,7 +373,7 @@ inline LLVector4 operator-(const LLVector4& a, const LLVector4& b) inline F32 operator*(const LLVector4& a, const LLVector4& b) { - return (a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]); + return a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]; } inline LLVector4 operator%(const LLVector4& a, const LLVector4& b) @@ -495,11 +446,7 @@ inline const LLVector4& operator*=(LLVector4& a, F32 k) inline const LLVector4& operator/=(LLVector4& a, F32 k) { - F32 t = 1.f / k; - a.mV[VX] *= t; - a.mV[VY] *= t; - a.mV[VZ] *= t; - return a; + return a *= 1.f / k; } inline LLVector4 operator-(const LLVector4& a) @@ -517,16 +464,16 @@ inline LLVector4::operator glm::vec4() const return glm::make_vec4(mV); } -inline F32 dist_vec(const LLVector4& a, const LLVector4& b) +inline F32 dist_vec(const LLVector4& a, const LLVector4& b) { LLVector4 vec = a - b; - return (vec.length()); + return vec.length(); } -inline F32 dist_vec_squared(const LLVector4& a, const LLVector4& b) +inline F32 dist_vec_squared(const LLVector4& a, const LLVector4& b) { LLVector4 vec = a - b; - return (vec.lengthSquared()); + return vec.lengthSquared(); } inline LLVector4 lerp(const LLVector4& a, const LLVector4& b, F32 u) @@ -538,17 +485,13 @@ inline LLVector4 lerp(const LLVector4& a, const LLVector4& b, F32 u) a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u); } -inline F32 LLVector4::normalize() +inline F32 LLVector4::normalize() { F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); - F32 oomag; if (mag > FP_MAG_THRESHOLD) { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; - mV[VZ] *= oomag; + *this /= mag; } else { @@ -557,30 +500,13 @@ inline F32 LLVector4::normalize() mV[VZ] = 0.f; mag = 0.f; } - return (mag); + return mag; } // deprecated -inline F32 LLVector4::normVec() +inline F32 LLVector4::normVec() { - F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]); - F32 oomag; - - if (mag > FP_MAG_THRESHOLD) - { - oomag = 1.f/mag; - mV[VX] *= oomag; - mV[VY] *= oomag; - mV[VZ] *= oomag; - } - else - { - mV[VX] = 0.f; - mV[VY] = 0.f; - mV[VZ] = 0.f; - mag = 0.f; - } - return (mag); + return normalize(); } // Because apparently some parts of the viewer use this for color info. diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 6a663a8e97..563dd9459c 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -50,7 +50,7 @@ static const U32 DEFAULT_POOL_SIZE = 5; // SL-14399: When we teleport to a brand-new simulator, the coprocedure queue // gets absolutely slammed with fetch requests. Make this queue effectively // unlimited. -const U32 LLCoprocedureManager::DEFAULT_QUEUE_SIZE = 1024*1024; +const U32 LLCoprocedureManager::DEFAULT_QUEUE_SIZE = 1024*512; //========================================================================= class LLCoprocedurePool: private boost::noncopyable @@ -58,7 +58,7 @@ class LLCoprocedurePool: private boost::noncopyable public: typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t; - LLCoprocedurePool(const std::string &name, size_t size); + LLCoprocedurePool(const std::string &name, size_t size, size_t queue_size); ~LLCoprocedurePool(); /// Places the coprocedure on the queue for processing. @@ -118,7 +118,7 @@ private: typedef std::shared_ptr CoprocQueuePtr; std::string mPoolName; - size_t mPoolSize, mActiveCoprocsCount, mPending; + size_t mPoolSize, mQueueSize, mActiveCoprocsCount, mPending; CoprocQueuePtr mPendingCoprocs; LLTempBoundListener mStatusListener; @@ -141,7 +141,7 @@ LLCoprocedureManager::~LLCoprocedureManager() close(); } -void LLCoprocedureManager::initializePool(const std::string &poolName) +void LLCoprocedureManager::initializePool(const std::string &poolName, size_t queue_size) { poolMap_t::iterator it = mPoolMap.find(poolName); @@ -180,7 +180,7 @@ void LLCoprocedureManager::initializePool(const std::string &poolName) LL_WARNS("CoProcMgr") << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; } - poolPtr_t pool(new LLCoprocedurePool(poolName, size)); + poolPtr_t pool(new LLCoprocedurePool(poolName, size, queue_size)); LL_ERRS_IF(!pool, "CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; bool inserted = mPoolMap.emplace(poolName, pool).second; @@ -212,7 +212,8 @@ void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpd mPropertyQueryFn = queryfn; mPropertyDefineFn = updatefn; - initializePool("Upload"); + constexpr size_t UPLOAD_QUEUE_SIZE = 2048; + initializePool("Upload", UPLOAD_QUEUE_SIZE); initializePool("AIS"); // it might be better to have some kind of on-demand initialization for AIS // "ExpCache" pool gets initialized in LLExperienceCache // asset storage pool gets initialized in LLViewerAssetStorage @@ -296,17 +297,19 @@ void LLCoprocedureManager::close(const std::string &pool) } //========================================================================= -LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): +LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size, size_t queue_size): mPoolName(poolName), mPoolSize(size), + mQueueSize(queue_size), mActiveCoprocsCount(0), mPending(0), mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), mCoroMapping() { + llassert_always(mQueueSize > mPoolSize); // queue should be able to fit pool try { - mPendingCoprocs = std::make_shared(LLCoprocedureManager::DEFAULT_QUEUE_SIZE); + mPendingCoprocs = std::make_shared(mQueueSize); // store in our LLTempBoundListener so that when the LLCoprocedurePool is // destroyed, we implicitly disconnect from this LLEventPump // Monitores application status @@ -357,7 +360,7 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); } - LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << LLCoprocedureManager::DEFAULT_QUEUE_SIZE << LL_ENDL; + LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << mQueueSize << LL_ENDL; } LLCoprocedurePool::~LLCoprocedurePool() @@ -376,7 +379,7 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced << "\" at " << mPending << LL_ENDL; - if (mPending >= (LLCoprocedureManager::DEFAULT_QUEUE_SIZE - 1)) + if (mPending >= (mQueueSize - 1)) { // If it's all used up (not supposed to happen, // fetched should cap it), we are going to crash diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 6c6e506654..485333657c 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -79,7 +79,7 @@ public: void close(); void close(const std::string &pool); - void initializePool(const std::string &poolName); + void initializePool(const std::string &poolName, size_t queue_size = DEFAULT_QUEUE_SIZE); private: diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 83a070df32..78cca47456 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -110,7 +110,8 @@ void LLExperienceCache::initSingleton() cache_stream >> (*this); } - LLCoprocedureManager::instance().initializePool("ExpCache"); + constexpr size_t CORO_QUEUE_SIZE = 2048; + LLCoprocedureManager::instance().initializePool("ExpCache", CORO_QUEUE_SIZE); LLCoros::instance().launch("LLExperienceCache::idleCoro", boost::bind(&LLExperienceCache::idleCoro, this)); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index a33f25307e..00ef79ce7f 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -66,7 +66,12 @@ LLModel::~LLModel() { if (mDecompID >= 0) { - LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID); + // can be null on shutdown + LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); + if (decomp) + { + decomp->deleteDecomposition(mDecompID); + } } mPhysics.mMesh.clear(); } diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 38bc5ff331..0b0d69812f 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -492,22 +492,6 @@ U32 LLRenderTarget::getNumTextures() const void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilterOptions filter_options) { gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index), filter_options == LLTexUnit::TFO_TRILINEAR || filter_options == LLTexUnit::TFO_ANISOTROPIC); - - bool isSRGB = false; - llassert(mInternalFormat.size() > index); - switch (mInternalFormat[index]) - { - case GL_SRGB: - case GL_SRGB8: - case GL_SRGB_ALPHA: - case GL_SRGB8_ALPHA8: - isSRGB = true; - break; - - default: - break; - } - gGL.getTexUnit(channel)->setTextureFilteringOption(filter_options); } diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index 3fdcf9f7f2..bb0b8ce04f 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -140,7 +140,7 @@ public: S32 notify(const LLSD& info); bool notifyChildren(const LLSD& info); - void draw(); + virtual void draw(); void storeOpenCloseState(); void restoreOpenCloseState(); diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 8178bada42..5be20bd314 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1346,9 +1346,17 @@ bool LLFlatListViewEx::getForceShowingUnmatchedItems() const return mForceShowingUnmatchedItems; } -void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show) +void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show, bool notify_parent) +{ + if (mForceShowingUnmatchedItems != show) { mForceShowingUnmatchedItems = show; + if (!mFilterSubString.empty()) + { + updateNoItemsMessage(mFilterSubString); + filterItems(false, true); + } + } } void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent) @@ -1416,6 +1424,7 @@ void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent) if (visibility_changed && notify_parent) { + rearrangeItems(); notifyParentItemsRectChanged(); } } diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index 6271231183..1f22360a8a 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -484,7 +484,11 @@ public: bool getForceShowingUnmatchedItems() const; - void setForceShowingUnmatchedItems(bool show); + /** + * Sets filtered out items to stay visible. Can result in rect changes, + * so can notify_parent if rect changes + */ + void setForceShowingUnmatchedItems(bool show, bool notify_parent); /** * Sets up new filter string and filters the list. diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index bdce9dec54..368a86ea84 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -221,6 +221,7 @@ public: void scrollToShowSelection(); void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } + LLScrollContainer* getScrollContainer() { return mScrollContainer; } LLRect getVisibleRect(); bool search(LLFolderViewItem* first_item, const std::string &search_string, bool backward); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 6d0cfcba95..9ca77dbe46 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -31,11 +31,12 @@ #include "llfolderviewitem.h" #include "llfolderview.h" #include "llfolderviewmodel.h" -#include "llpanel.h" #include "llcallbacklist.h" #include "llcriticaldamp.h" #include "llclipboard.h" #include "llfocusmgr.h" // gFocusMgr +#include "llnotificationsutil.h" +#include "llpanel.h" #include "lltrans.h" #include "llwindow.h" @@ -60,7 +61,11 @@ LLUIColor LLFolderViewItem::sSearchStatusColor; S32 LLFolderViewItem::sTopPad = 0; LLUIImagePtr LLFolderViewItem::sFolderArrowImg; LLUIImagePtr LLFolderViewItem::sSelectionImg; +LLUIImagePtr LLFolderViewItem::sFavoriteImg; +LLUIImagePtr LLFolderViewItem::sFavoriteContentImg; LLFontGL* LLFolderViewItem::sSuffixFont = nullptr; +LLUIColor LLFolderViewItem::sFavoriteColor; +bool LLFolderViewItem::sColorSetInitialized = false; // only integers can be initialized in header const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; @@ -68,6 +73,9 @@ const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f; const LLColor4U DEFAULT_WHITE(255, 255, 255); +constexpr S32 FAVORITE_IMAGE_SIZE = 14; +constexpr S32 FAVORITE_IMAGE_PAD = 3; + //static LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) @@ -102,6 +110,8 @@ void LLFolderViewItem::initClass() sTopPad = default_params.item_top_pad; sFolderArrowImg = default_params.folder_arrow_image; sSelectionImg = default_params.selection_image; + sFavoriteImg = default_params.favorite_image; + sFavoriteContentImg = default_params.favorite_content_image; sSuffixFont = getLabelFontForStyle(LLFontGL::NORMAL); sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); @@ -121,6 +131,8 @@ void LLFolderViewItem::cleanupClass() sFonts.clear(); sFolderArrowImg = nullptr; sSelectionImg = nullptr; + sFavoriteImg = nullptr; + sFavoriteContentImg = nullptr; sSuffixFont = nullptr; } @@ -129,13 +141,15 @@ void LLFolderViewItem::cleanupClass() LLFolderViewItem::Params::Params() : root(), listener(), + favorite_image("favorite_image"), + favorite_content_image("favorite_content_image"), folder_arrow_image("folder_arrow_image"), folder_indentation("folder_indentation"), selection_image("selection_image"), item_height("item_height"), item_top_pad("item_top_pad"), creation_date(), - allow_wear("allow_wear", true), + marketplace_item("marketplace_item", false), allow_drop("allow_drop", true), font_color("font_color"), font_highlight_color("font_highlight_color"), @@ -155,6 +169,8 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) : LLView(p), mLabelWidth(0), mLabelWidthDirty(false), + mIsFavorite(false), + mHasFavorites(false), mSuffixNeedsRefresh(false), mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), mParentFolder( NULL ), @@ -175,7 +191,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mRoot(p.root), mViewModelItem(p.listener), mIsMouseOverTitle(false), - mAllowWear(p.allow_wear), + mMarketplaceItem(p.marketplace_item), mAllowDrop(p.allow_drop), mFontColor(p.font_color), mFontHighlightColor(p.font_highlight_color), @@ -189,6 +205,21 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mMaxFolderItemOverlap(p.max_folder_item_overlap), mDoubleClickOverride(p.double_click_override) { + if (!sColorSetInitialized) + { + sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); + sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); + sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); + sFavoriteColor = LLUIColorTable::instance().getColor("InventoryFavoriteColor", DEFAULT_WHITE); + sColorSetInitialized = true; + } + if (mViewModelItem) { mViewModelItem->setFolderViewItem(this); @@ -211,6 +242,7 @@ bool LLFolderViewItem::postBuild() // getDisplayName() is expensive (due to internal getLabelSuffix() and name building) // it also sets search strings so it requires a filter reset mLabel = utf8str_to_wstring(vmi->getDisplayName()); + mIsFavorite = vmi->isFavorite() && !vmi->isItemInTrash(); setToolTip(vmi->getName()); // Dirty the filter flag of the model from the view (CHUI-849) @@ -325,6 +357,7 @@ void LLFolderViewItem::refresh() mLabel = utf8str_to_wstring(vmi.getDisplayName()); mLabelFontBuffer.reset(); + mIsFavorite = vmi.isFavorite() && !vmi.isItemInTrash(); setToolTip(vmi.getName()); // icons are slightly expensive to get, can be optimized // see LLInventoryIcon::getIcon() @@ -359,6 +392,8 @@ void LLFolderViewItem::refreshSuffix() mIconOpen = vmi->getIconOpen(); mIconOverlay = vmi->getIconOverlay(); + mIsFavorite = vmi->isFavorite() && !vmi->isItemInTrash(); + if (mRoot->useLabelSuffix()) { // Very Expensive! @@ -428,6 +463,10 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height ) } mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel.c_str()) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix.c_str()) + mLabelPaddingRight; mLabelWidthDirty = false; + if (mIsFavorite) + { + mLabelWidth += FAVORITE_IMAGE_SIZE + FAVORITE_IMAGE_PAD; + } } *width = llmax(*width, mLabelWidth); @@ -554,10 +593,15 @@ void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) void LLFolderViewItem::openItem( void ) { - if (mAllowWear || !getViewModelItem()->isItemWearable()) + if (!mMarketplaceItem || !getViewModelItem()->isItemWearable()) { getViewModelItem()->openItem(); } + else if (mMarketplaceItem) + { + // Wearing an object from any listing, active or not, is verbotten + LLNotificationsUtil::add("AlertMerchantListingCannotWear"); + } } void LLFolderViewItem::rename(const std::string& new_name) @@ -771,6 +815,45 @@ void LLFolderViewItem::drawOpenFolderArrow() } } +void LLFolderViewItem::drawFavoriteIcon() +{ + static LLUICachedControl draw_star("InventoryFavoritesUseStar", true); + static LLUICachedControl draw_hollow_star("InventoryFavoritesUseHollowStar", true); + + LLUIImage* favorite_image = nullptr; + if (draw_star && mIsFavorite) + { + favorite_image = sFavoriteImg; + } + else if (draw_hollow_star && mHasFavorites && !isOpen()) + { + favorite_image = sFavoriteContentImg; + } + + if (favorite_image) + { + S32 x_offset = 0; + LLScrollContainer* scroll = mRoot->getScrollContainer(); + if (scroll) + { + S32 width = scroll->getVisibleContentRect().getWidth(); + S32 offset = scroll->getDocPosHorizontal(); + x_offset = width + offset; + } + else + { + x_offset = getRect().getWidth(); + } + gl_draw_scaled_image( + x_offset - FAVORITE_IMAGE_SIZE - FAVORITE_IMAGE_PAD, + getRect().getHeight() - mItemHeight + FAVORITE_IMAGE_PAD, + FAVORITE_IMAGE_SIZE, + FAVORITE_IMAGE_SIZE, + favorite_image->getImage(), + sFgColor); + } +} + /*virtual*/ bool LLFolderViewItem::isHighlightAllowed() { return mIsSelected; @@ -928,6 +1011,7 @@ void LLFolderViewItem::draw() { drawOpenFolderArrow(); } + drawFavoriteIcon(); drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); @@ -999,7 +1083,20 @@ void LLFolderViewItem::draw() } } - LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; + static LLUICachedControl highlight_color("InventoryFavoritesColorText", true); + LLColor4 color; + if (mIsSelected && filled) + { + color = mFontHighlightColor; + } + else if (mIsFavorite && highlight_color) + { + color = sFavoriteColor; + } + else + { + color = mFontColor; + } if (isFadeItem()) { @@ -1093,7 +1190,8 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): mIsFolderComplete(false), // folder might have children that are not loaded yet. mAreChildrenInited(false), // folder might have children that are not built yet. mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0) + mLastCalculatedWidth(0), + mFavoritesDirtyFlags(0) { } @@ -1119,6 +1217,11 @@ LLFolderViewFolder::~LLFolderViewFolder( void ) // The LLView base class takes care of object destruction. make sure that we // don't have mouse or keyboard focus gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + + if (mFavoritesDirtyFlags) + { + gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, this); + } } // addToFolder() returns true if it succeeds. false otherwise @@ -1762,6 +1865,140 @@ bool LLFolderViewFolder::isMovable() return true; } +void LLFolderViewFolder::updateHasFavorites(bool new_childs_value) +{ + if (mFavoritesDirtyFlags == 0) + { + gIdleCallbacks.addFunction(&LLFolderViewFolder::onIdleUpdateFavorites, this); + } + if (new_childs_value) + { + mFavoritesDirtyFlags |= FAVORITE_ADDED; + } + else + { + mFavoritesDirtyFlags |= FAVORITE_REMOVED; + } +} + +void LLFolderViewFolder::onIdleUpdateFavorites(void* data) +{ + LLFolderViewFolder* self = reinterpret_cast(data); + if (self->mFavoritesDirtyFlags == 0) + { + // already processed either on previous run or by a different callback + gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self); + return; + } + + if (self->getViewModelItem()->isItemInTrash()) + { + // do not display favorite-stars in trash + self->mFavoritesDirtyFlags = 0; + gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self); + return; + } + + if (self->mFavoritesDirtyFlags == FAVORITE_ADDED) + { + if (!self->mHasFavorites) + { + // propagate up, exclude root + LLFolderViewFolder* parent = self; + while (parent + && (!parent->hasFavorites() || parent->mFavoritesDirtyFlags) + && !parent->getViewModelItem()->isAgentInventoryRoot()) + { + parent->setHasFavorites(true); + if (parent->mFavoritesDirtyFlags) + { + // Parent will remove onIdleUpdateFavorites later, don't remove now, + // We are inside gIdleCallbacks. Removing 'self' callback is safe, + // but removing 'parent' can invalidate following iterator + parent->mFavoritesDirtyFlags = 0; + } + parent = parent->getParentFolder(); + } + } + else + { + // already up to date + self->mFavoritesDirtyFlags = 0; + gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self); + } + } + else if (self->mFavoritesDirtyFlags > FAVORITE_ADDED) + { + // full check + LLFolderViewFolder* parent = self; + while (parent && !parent->getViewModelItem()->isAgentInventoryRoot()) + { + bool has_favorites = false; + for (items_t::iterator iter = parent->mItems.begin(); + iter != parent->mItems.end();) + { + items_t::iterator iit = iter++; + if ((*iit)->isFavorite()) + { + has_favorites = true; + break; + } + } + + for (folders_t::iterator iter = parent->mFolders.begin(); + iter != parent->mFolders.end() && !has_favorites;) + { + folders_t::iterator fit = iter++; + if ((*fit)->isFavorite() || (*fit)->hasFavorites()) + { + has_favorites = true; + break; + } + } + + if (!has_favorites) + { + if (parent->hasFavorites()) + { + parent->setHasFavorites(false); + } + else + { + // Nothing changed + break; + } + } + else + { + // propagate up, exclude root + while (parent + && (!parent->hasFavorites() || parent->mFavoritesDirtyFlags) + && !parent->getViewModelItem()->isAgentInventoryRoot()) + { + parent->setHasFavorites(true); + if (parent->mFavoritesDirtyFlags) + { + // Parent will remove onIdleUpdateFavorites later, don't remove now, + // We are inside gIdleCallbacks. Removing 'self' callback is safe, + // but removing 'parent' can invalidate following iterator + parent->mFavoritesDirtyFlags = 0; + } + parent = parent->getParentFolder(); + } + break; + } + if (parent->mFavoritesDirtyFlags) + { + // Parent will remove onIdleUpdateFavorites later, don't remove now. + // We are inside gIdleCallbacks. Removing 'self' callback is safe, + // but removing 'parent' can invalidate following iterator + parent->mFavoritesDirtyFlags = 0; + } + parent = parent->getParentFolder(); + } + } +} + bool LLFolderViewFolder::isRemovable() { diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 2ee018a90a..c9b003b892 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -50,7 +50,9 @@ class LLFolderViewItem : public LLView public: struct Params : public LLInitParam::Block { - Optional folder_arrow_image, + Optional favorite_image, + favorite_content_image, + folder_arrow_image, selection_image; Mandatory root; Mandatory listener; @@ -60,7 +62,7 @@ public: item_top_pad; Optional creation_date; - Optional allow_wear; + Optional marketplace_item; Optional allow_drop; Optional font_color; @@ -93,6 +95,8 @@ protected: LLWString mLabel; S32 mLabelWidth; bool mLabelWidthDirty; + bool mIsFavorite; + bool mHasFavorites; S32 mLabelPaddingRight; LLFolderViewFolder* mParentFolder; LLPointer mViewModelItem; @@ -122,7 +126,7 @@ protected: mIsCurSelection, mDragAndDropTarget, mIsMouseOverTitle, - mAllowWear, + mMarketplaceItem, mAllowDrop, mSingleFolderMode, mDoubleClickOverride, @@ -133,6 +137,7 @@ protected: LLUIColor mFontColor; LLUIColor mFontHighlightColor; + static bool sColorSetInitialized; // For now assuming all colors are the same in derived classes. static LLUIColor sFgColor; @@ -145,6 +150,8 @@ protected: static LLUIColor sFilterTextColor; static LLUIColor sSuffixColor; static LLUIColor sSearchStatusColor; + static LLUIColor sFavoriteColor; + // this is an internal method used for adding items to folders. A // no-op at this level, but reimplemented in derived classes. @@ -208,6 +215,8 @@ public: // Returns true is this object and all of its children can be moved virtual bool isMovable(); + bool isFavorite() const { return mIsFavorite; } + // destroys this item recursively virtual void destroyView(); @@ -298,6 +307,7 @@ public: // virtual void handleDropped(); virtual void draw(); void drawOpenFolderArrow(); + void drawFavoriteIcon(); void drawHighlight(bool showContent, bool hasKeyboardFocus, const LLUIColor& selectColor, const LLUIColor& flashColor, const LLUIColor& outlineColor, const LLUIColor& mouseOverColor); void drawLabel(const LLFontGL* font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, @@ -311,6 +321,8 @@ private: static S32 sTopPad; static LLUIImagePtr sFolderArrowImg; static LLUIImagePtr sSelectionImg; + static LLUIImagePtr sFavoriteImg; + static LLUIImagePtr sFavoriteContentImg; static LLFontGL* sSuffixFont; LLFontVertexBuffer mLabelFontBuffer; @@ -400,6 +412,18 @@ public: // Returns true is this object and all of its children can be moved virtual bool isMovable(); + bool isFavorite() const { return mIsFavorite; } + bool hasFavorites() const { return mHasFavorites; } + void setHasFavorites(bool val) { mHasFavorites = val; } + void updateHasFavorites(bool new_childs_value); +private: + static void onIdleUpdateFavorites(void* data); + + constexpr static S32 FAVORITE_ADDED = 1; + constexpr static S32 FAVORITE_REMOVED = 2; + S32 mFavoritesDirtyFlags { 0 }; +public: + // destroys this folder, and all children virtual void destroyView(); void destroyRoot(); diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 9372818ca5..2865b789b9 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -162,6 +162,7 @@ public: virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0; + virtual bool isFavorite() const = 0; virtual bool isItemWearable() const { return false; } virtual bool isItemRenameable() const = 0; @@ -170,7 +171,8 @@ public: virtual bool isItemMovable( void ) const = 0; // Can be moved to another folder virtual void move( LLFolderViewModelItem* parent_listener ) = 0; - virtual bool isItemRemovable( bool check_worn = true ) const = 0; // Can be destroyed + virtual bool isItemRemovable( bool check_worn = true) const = 0; // Can be destroyed + virtual bool isItemInTrash(void) const = 0; virtual bool removeItem() = 0; virtual void removeBatch(std::vector& batch) = 0; @@ -183,6 +185,9 @@ public: virtual void pasteFromClipboard() = 0; virtual void pasteLinkFromClipboard() = 0; + virtual bool isAgentInventory() const = 0; + virtual bool isAgentInventoryRoot() const = 0; + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet? @@ -219,6 +224,7 @@ public: virtual S32 getSortVersion() = 0; virtual void setSortVersion(S32 version) = 0; virtual void setParent(LLFolderViewModelItem* parent) = 0; + virtual const LLFolderViewModelItem* getParent() = 0; virtual bool hasParent() = 0; protected: @@ -249,14 +255,14 @@ public: mChildren.clear(); } - void requestSort() { mSortVersion = -1; } - S32 getSortVersion() { return mSortVersion; } - void setSortVersion(S32 version) { mSortVersion = version;} + void requestSort() override { mSortVersion = -1; } + S32 getSortVersion() override { return mSortVersion; } + void setSortVersion(S32 version) override { mSortVersion = version;} - S32 getLastFilterGeneration() const { return mLastFilterGeneration; } + S32 getLastFilterGeneration() const override { return mLastFilterGeneration; } S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; } - S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; } - void dirtyFilter() + S32 getMarkedDirtyGeneration() const override { return mMarkedDirtyGeneration; } + void dirtyFilter() override { if(mMarkedDirtyGeneration < 0) { @@ -271,7 +277,7 @@ public: mParent->dirtyFilter(); } } - void dirtyDescendantsFilter() + void dirtyDescendantsFilter() override { mMostFilteredDescendantGeneration = -1; if (mParent) @@ -279,13 +285,13 @@ public: mParent->dirtyDescendantsFilter(); } } - bool hasFilterStringMatch(); - std::string::size_type getFilterStringOffset(); - std::string::size_type getFilterStringSize(); + bool hasFilterStringMatch() override; + std::string::size_type getFilterStringOffset() override; + std::string::size_type getFilterStringSize() override; - typedef std::list child_list_t; + typedef std::list > child_list_t; - virtual void addChild(LLFolderViewModelItem* child) + virtual void addChild(LLFolderViewModelItem* child) override { mChildren.push_back(child); child->setParent(this); @@ -293,15 +299,15 @@ public: requestSort(); } - virtual void removeChild(LLFolderViewModelItem* child) + virtual void removeChild(LLFolderViewModelItem* child) override final { - mChildren.remove(child); child->setParent(NULL); + mChildren.remove(child); dirtyDescendantsFilter(); dirtyFilter(); } - virtual void clearChildren() + virtual void clearChildren() override { // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); }); @@ -314,7 +320,7 @@ public: child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); } child_list_t::size_type getChildrenCount() const { return mChildren.size(); } - void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) + void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) override { mPassedFilter = passed; mLastFilterGeneration = filter_generation; @@ -323,20 +329,20 @@ public: mMarkedDirtyGeneration = -1; } - void setPassedFolderFilter(bool passed, S32 filter_generation) + void setPassedFolderFilter(bool passed, S32 filter_generation) override { mPassedFolderFilter = passed; mLastFolderFilterGeneration = filter_generation; } - virtual bool potentiallyVisible() + virtual bool potentiallyVisible() override { return passedFilter() // we've passed the filter || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet || descendantsPassedFilter(); } - virtual bool passedFilter(S32 filter_generation = -1) + virtual bool passedFilter(S32 filter_generation = -1) override { if (filter_generation < 0) { @@ -347,7 +353,7 @@ public: return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation)); } - virtual bool descendantsPassedFilter(S32 filter_generation = -1) + virtual bool descendantsPassedFilter(S32 filter_generation = -1) override { if (filter_generation < 0) { @@ -356,10 +362,10 @@ public: return mMostFilteredDescendantGeneration >= filter_generation; } - protected: - virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } - virtual bool hasParent() { return mParent != NULL; } + virtual void setParent(LLFolderViewModelItem* parent) override final { mParent = parent; } + virtual const LLFolderViewModelItem* getParent() override { return mParent; }; + virtual bool hasParent() override { return mParent != NULL; } S32 mSortVersion; bool mPassedFilter; @@ -376,7 +382,7 @@ protected: LLFolderViewModelItem* mParent; LLFolderViewModelInterface& mRootViewModel; - void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} + void setFolderViewItem(LLFolderViewItem* folder_view_item) override { mFolderViewItem = folder_view_item;} LLFolderViewItem* mFolderViewItem; }; @@ -390,15 +396,15 @@ public: mFolderView(NULL) {} - virtual void requestSortAll() + virtual void requestSortAll() override { // sort everything mTargetSortVersion++; } - virtual std::string getStatusText(bool is_empty_folder = false); - virtual void filter(); + virtual std::string getStatusText(bool is_empty_folder = false) override; + virtual void filter() override; - void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} + void setFolderView(LLFolderView* folder_view) override { mFolderView = folder_view;} protected: bool needsSort(class LLFolderViewModelItem* item); @@ -428,14 +434,14 @@ public: virtual const SortType& getSorter() const { return *mSorter; } virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); } - virtual FilterType& getFilter() { return *mFilter; } - virtual const FilterType& getFilter() const { return *mFilter; } + virtual FilterType& getFilter() override { return *mFilter; } + virtual const FilterType& getFilter() const override { return *mFilter; } virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); } // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, // this method needs to be overloaded and return the relevant fetch status. - virtual bool contentsReady() { return true; } - virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; } + virtual bool contentsReady() override { return true; } + virtual bool isFolderComplete(LLFolderViewFolder* folder) override { return true; } struct ViewModelCompare { @@ -456,7 +462,7 @@ public: const SortType& mSorter; }; - void sort(LLFolderViewFolder* folder) + void sort(LLFolderViewFolder* folder) override { if (needsSort(folder->getViewModelItem())) { diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index c11b42a348..2ca2454040 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -46,6 +46,7 @@ #include "llfocusmgr.h" #include "llcoord.h" #include "llwindow.h" +#include "llemojihelper.h" #include "llcriticaldamp.h" #include "lluictrlfactory.h" @@ -1411,6 +1412,7 @@ void LLMenuItemBranchDownGL::openMenu( void ) } else { + LLEmojiHelper::instance().hideHelper(nullptr, true); if (branch->getTornOff()) { LLFloater * branch_parent = dynamic_cast(branch->getParent()); diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index c5c31f7252..225ff607ad 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -28,6 +28,7 @@ #include "llmodaldialog.h" +#include "llemojihelper.h" #include "llfocusmgr.h" #include "v4color.h" #include "v2math.h" @@ -35,6 +36,7 @@ #include "llwindow.h" #include "llkeyboard.h" #include "llmenugl.h" + // static std::list LLModalDialog::sModalStack; @@ -98,7 +100,7 @@ void LLModalDialog::onOpen(const LLSD& key) { if (mModal) { - // If Modal, Hide the active modal dialog + // If Modal, hide the active modal dialog if (!sModalStack.empty()) { LLModalDialog* front = sModalStack.front(); @@ -155,6 +157,12 @@ void LLModalDialog::setVisible( bool visible ) { if( visible ) { + // Hide all menus currently shown + LLMenuGL::sMenuContainer->hideMenus(); + + // Hide EmojiPicker if it is shown + LLEmojiHelper::instance().hideHelper(nullptr, true); + // This is a modal dialog. It sucks up all mouse and keyboard operations. gFocusMgr.setMouseCapture( this ); @@ -301,7 +309,6 @@ void LLModalDialog::centerOnScreen() centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); } - // static void LLModalDialog::onAppFocusLost() { @@ -333,6 +340,7 @@ void LLModalDialog::onAppFocusGained() } } +// static void LLModalDialog::shutdownModals() { // This method is only for use during app shutdown. ~LLModalDialog() diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 7007049e1c..d5755ae4b6 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2227,6 +2227,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url)); registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url)); registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url)); + registrar.add("Url.ZoomInObject", boost::bind(&LLUrlAction::zoomInObject, url)); registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); registrar.add("Url.ShowParcelOnMap", boost::bind(&LLUrlAction::showParcelOnMap, url)); registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index cfe729be06..58b785fcdd 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -274,7 +274,9 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mShowChatMentionPicker(false), mEnableTooltipPaste(p.enable_tooltip_paste), mPassDelete(false), - mKeepSelectionOnReturn(false) + mKeepSelectionOnReturn(false), + mSelectAllOnFocusReceived(false), + mSelectedOnFocusReceived(false) { mSourceID.generate(); @@ -398,6 +400,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insen setCursorPos(loc); mIsSelecting = true; + mSelectedOnFocusReceived = false; mSelectionEnd = mCursorPos; mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size())); } @@ -677,6 +680,13 @@ bool LLTextEditor::canSelectAll() const return true; } +//virtual +void LLTextEditor::deselect() +{ + LLTextBase::deselect(); + mSelectedOnFocusReceived = false; +} + // virtual void LLTextEditor::selectAll() { @@ -694,6 +704,11 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p endSelection(); } +void LLTextEditor::setSelectAllOnFocusReceived(bool b) +{ + mSelectAllOnFocusReceived = b; +} + void LLTextEditor::insertEmoji(llwchar emoji) { LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL; @@ -795,8 +810,16 @@ bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) // Delay cursor flashing resetCursorBlink(); + mSelectedOnFocusReceived = false; if (handled && !gFocusMgr.getMouseCapture()) { + if (!mask && mSelectAllOnFocusReceived) + { + mIsSelecting = false; + mSelectionStart = getLength(); + mSelectionEnd = 0; + mSelectedOnFocusReceived = true; + } gFocusMgr.setMouseCapture( this ); } return handled; @@ -2200,6 +2223,11 @@ void LLTextEditor::focusLostHelper() gEditMenuHandler = NULL; } + if (mSelectedOnFocusReceived) + { + deselect(); + } + if (mCommitOnFocusLost) { onCommit(); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 882bb145df..cdfcbcdd63 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -144,8 +144,10 @@ public: virtual bool canDoDelete() const; virtual void selectAll(); virtual bool canSelectAll() const; + virtual void deselect(); void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos); + void setSelectAllOnFocusReceived(bool b); virtual bool canLoadOrSaveToFile(); @@ -336,6 +338,8 @@ private: bool mEnableTooltipPaste; bool mPassDelete; bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter + bool mSelectAllOnFocusReceived; + bool mSelectedOnFocusReceived; LLUUID mSourceID; diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp index ce599a7552..8b320b59cc 100644 --- a/indra/llui/llurlaction.cpp +++ b/indra/llui/llurlaction.cpp @@ -117,6 +117,16 @@ void LLUrlAction::teleportToLocation(std::string url) } } +void LLUrlAction::zoomInObject(std::string url) +{ + LLUrlMatch match; + std::string object_id = getObjectId(url); + if (LLUUID::validate(object_id) && LLUrlRegistry::instance().findUrl(url, match)) + { + executeSLURL("secondlife:///app/object/" + object_id + "/zoomin/" + match.getLocation()); + } +} + void LLUrlAction::showLocationOnMap(std::string url) { LLUrlMatch match; @@ -160,6 +170,16 @@ void LLUrlAction::copyLabelToClipboard(std::string url) } } +std::string LLUrlAction::getURLLabel(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.getLabel(); + } + return ""; +} + void LLUrlAction::showProfile(std::string url) { // Get id from 'secondlife:///app/{cmd}/{id}/{action}' diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h index c960d61ca0..56d459bb26 100644 --- a/indra/llui/llurlaction.h +++ b/indra/llui/llurlaction.h @@ -60,6 +60,10 @@ public: /// if the Url specifies an SL location, teleport there static void teleportToLocation(std::string url); + /// If the Url specifies an object id, attempt to zoom in. + /// If not possible to zoom in, show on map + static void zoomInObject(std::string url); + /// if the Url specifies an SL location, show it on a map static void showLocationOnMap(std::string url); @@ -74,6 +78,8 @@ public: /// copy a Url to the clipboard static void copyURLToClipboard(std::string url); + static std::string getURLLabel(std::string url); + /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile static void showProfile(std::string url); static std::string getUserID(std::string url); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 59e8f47c8f..95603d7ed5 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -43,8 +43,6 @@ #include "llexperiencecache.h" #include "v3dmath.h" -#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" - // Utility functions std::string localize_slapp_label(const std::string& url, const std::string& full_name); diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index efa79c258d..efb5081103 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -43,6 +43,8 @@ class LLAvatarName; class LLVector3d; +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + typedef boost::signals2::signal LLUrlLabelSignal; diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 28639b9af0..20951ff816 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -904,12 +904,12 @@ void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable) // set_enabled shouldn't be done on the worker thread. if (mPeerConnection) { + mPeerConnection->SetAudioRecording(enable); auto senders = mPeerConnection->GetSenders(); for (auto &sender : senders) { sender->track()->set_enabled(enable); } - mPeerConnection->SetAudioRecording(enable); } } @@ -964,6 +964,9 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute) { if (mPeerConnection) { + // SetAudioRecording must be called before enabling/disabling tracks. + mPeerConnection->SetAudioRecording(enable); + auto senders = mPeerConnection->GetSenders(); RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size(); @@ -982,7 +985,6 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute) track->set_enabled(enable); } } - mPeerConnection->SetAudioRecording(enable); } }); } diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 68b6de197a..94fd104bcc 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -371,10 +371,14 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool LLWindowWin32Thread(); void run() override; - void close() override; - // closes queue, wakes thread, waits until thread closes - void wakeAndDestroy(); + // Detroys handles and window + // Either post to or call from window thread + void destroyWindow(); + + // Closes queue, wakes thread, waits until thread closes. + // Call from main thread + bool wakeAndDestroy(); void glReady() { @@ -431,6 +435,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 bool mGLReady = false; bool mGotGLBuffer = false; + LLAtomicBool mDeleteOnExit = false; }; @@ -874,6 +879,7 @@ LLWindowWin32::~LLWindowWin32() } delete mDragDrop; + mDragDrop = NULL; delete [] mWindowTitle; mWindowTitle = NULL; @@ -885,6 +891,7 @@ LLWindowWin32::~LLWindowWin32() mWindowClassName = NULL; delete mWindowThread; + mWindowThread = NULL; } void LLWindowWin32::show() @@ -993,7 +1000,7 @@ void LLWindowWin32::close() // Restore gamma to the system values. restoreGamma(); - LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; + LL_INFOS("Window") << "Destroying Window Thread" << LL_ENDL; if (sWindowHandleForMessageBox == mWindowHandle) { @@ -1003,7 +1010,11 @@ void LLWindowWin32::close() mhDC = NULL; mWindowHandle = NULL; - mWindowThread->wakeAndDestroy(); + if (mWindowThread->wakeAndDestroy()) + { + // thread will delete itselfs once done + mWindowThread = NULL; + } } bool LLWindowWin32::isValid() @@ -3134,10 +3145,14 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ break; } } - else + else // (NULL == window_imp) { - // (NULL == window_imp) LL_DEBUGS("Window") << "No window implementation to handle message with, message code: " << U32(u_msg) << LL_ENDL; + if (u_msg == WM_DESTROY) + { + PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0 + return 0; + } } // pass unhandled messages down to Windows @@ -4607,25 +4622,11 @@ std::vector LLWindowWin32::getDynamicFallbackFontList() #endif // LL_WINDOWS inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() - : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/) + : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false) { LL::ThreadPool::start(); } -void LLWindowWin32::LLWindowWin32Thread::close() -{ - if (!mQueue->isClosed()) - { - LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL; - LL::ThreadPool::close(); - - // Workaround for SL-18721 in case window closes too early and abruptly - LLSplashScreen::show(); - LLSplashScreen::update("..."); // will be updated later - } -} - - /** * LogChange is to log changes in status while trying to avoid spamming the * log with repeated messages, especially in a tight loop. It refuses to log @@ -4849,108 +4850,110 @@ void LLWindowWin32::LLWindowWin32Thread::run() } #endif } + + destroyWindow(); + + if (mDeleteOnExit) + { + delete this; + } } -void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() +void LLWindowWin32::LLWindowWin32Thread::destroyWindow() +{ + if (mWindowHandleThrd != NULL && IsWindow(mWindowHandleThrd)) + { + if (mhDCThrd) + { + if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + mhDCThrd = NULL; + } + + // This causes WM_DESTROY to be sent *immediately* + if (!destroy_window_handler(mWindowHandleThrd)) + { + LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; + } + } + else + { + // Something killed the window while we were busy destroying gl or handle somehow got broken + LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; + } + mWindowHandleThrd = NULL; + mhDCThrd = NULL; +} + +bool LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() { if (mQueue->isClosed()) { - LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL; - return; + LL_WARNS("Window") << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL; + return false; } - // Make sure we don't leave a blank toolbar button. - // Also hiding window now prevents user from suspending it - // via some action (like dragging it around) - ShowWindow(mWindowHandleThrd, SW_HIDE); + // Hide the window immediately to prevent user interaction during shutdown + if (mWindowHandleThrd) + { + ShowWindow(mWindowHandleThrd, SW_HIDE); + } + else + { + LL_WARNS("Window") << "Tried to hide window, but Win32 window handle is NULL." << LL_ENDL; + return false; + } - // Schedule destruction + mGLReady = false; + + // Capture current handle before we lose it HWND old_handle = mWindowHandleThrd; - post([this]() - { - if (IsWindow(mWindowHandleThrd)) - { - if (mhDCThrd) - { - if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) - { - LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; - } - mhDCThrd = NULL; - } - // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(mWindowHandleThrd)) - { - LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; - } - } - else - { - // Something killed the window while we were busy destroying gl or handle somehow got broken - LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; - } - mWindowHandleThrd = NULL; - mhDCThrd = NULL; - mGLReady = false; - }); + // Clear the user data to prevent callbacks from finding us + if (old_handle) + { + SetWindowLongPtr(old_handle, GWLP_USERDATA, NULL); + } + // Signal thread to clean up when done + mDeleteOnExit = true; + + // Close the queue first LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL; mQueue->close(); - // Post a nonsense user message to wake up the thread in - // case it is waiting for a getMessage() + // Wake up the thread if it's stuck in GetMessage() if (old_handle) { WPARAM wparam{ 0xB0B0 }; LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle << ", " << WM_DUMMY_ << ", " << wparam << ")" << std::dec << LL_ENDL; + + // Use PostMessage to signal thread to wake up PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); } - // There are cases where window will refuse to close, - // can't wait forever on join, check state instead - LLTimer timeout; - timeout.setTimerExpirySec(2.0); - while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd) + // Cleanly detach threads instead of joining them to avoid blocking the main thread + // This is acceptable since the thread will self-delete with mDeleteOnExit + for (auto& pair : mThreads) { - ms_sleep(100); + try { + // Only detach if the thread is joinable + if (pair.second.joinable()) + { + pair.second.detach(); + } + } + catch (const std::system_error& e) { + LL_WARNS("Window") << "Exception detaching thread: " << e.what() << LL_ENDL; + } } - if (getQueue().done() || mWindowHandleThrd == NULL) - { - // Window is closed, started closing or is cleaning up - // now wait for our single thread to die. - if (mWindowHandleThrd) - { - LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; - } - else - { - LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; - } - for (auto& pair : mThreads) - { - pair.second.join(); - } - } - else - { - // Something suspended window thread, can't afford to wait forever - // so kill thread instead - // Ex: This can happen if user starts dragging window arround (if it - // was visible) or a modal notification pops up - LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL; - - for (auto& pair : mThreads) - { - // very unsafe - TerminateThread(pair.second.native_handle(), 0); - pair.second.detach(); - } - } LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; + return true; } void LLWindowWin32::post(const std::function& func) diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index bb590ebd76..562a30e8d1 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -157,6 +157,9 @@ LLControlVariable::LLControlVariable(const std::string& name, eControlType type, { if ((persist != PERSIST_NO) && mComment.empty()) { + // File isn't actually missing, but something is wrong with it + // so the main point is to warn user to reinstall + LLError::LLUserWarningMsg::showMissingFiles(); LL_ERRS() << "Must supply a comment for control " << mName << LL_ENDL; } //Push back versus setValue'ing here, since we don't want to call a signal yet diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 759a40fc08..f2aec208a1 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -342,6 +342,7 @@ set(viewer_SOURCE_FILES llhudeffectpointat.cpp llhudeffecttrail.cpp llhudeffectblob.cpp + llhudeffectresetskeleton.cpp llhudicon.cpp llhudmanager.cpp llhudnametag.cpp @@ -367,6 +368,7 @@ set(viewer_SOURCE_FILES llinventorygallerymenu.cpp llinventoryicon.cpp llinventoryitemslist.cpp + llinventorylistener.cpp llinventorylistitem.cpp llinventorymodel.cpp llinventorymodelbackgroundfetch.cpp @@ -391,6 +393,7 @@ set(viewer_SOURCE_FILES llmaniprotate.cpp llmanipscale.cpp llmaniptranslate.cpp + llfloatermarketplace.cpp llmarketplacefunctions.cpp llmarketplacenotifications.cpp llmaterialeditor.cpp @@ -928,6 +931,7 @@ set(viewer_HEADER_FILES llfloaterlinkreplace.h llfloaterloadprefpreset.h llfloatermap.h + llfloatermarketplace.h llfloatermarketplacelistings.h llfloatermediasettings.h llfloatermemleak.h @@ -1016,6 +1020,7 @@ set(viewer_HEADER_FILES llhudeffectpointat.h llhudeffecttrail.h llhudeffectblob.h + llhudeffectresetskeleton.h llhudicon.h llhudmanager.h llhudnametag.h @@ -1040,6 +1045,7 @@ set(viewer_HEADER_FILES llinventorygallerymenu.h llinventoryicon.h llinventoryitemslist.h + llinventorylistener.h llinventorylistitem.h llinventorymodel.h llinventorymodelbackgroundfetch.h @@ -1416,7 +1422,7 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt" "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}\n") set_source_files_properties( - llversioninfo.cpp tests/llversioninfo_test.cpp + llversioninfo.cpp tests/llversioninfo_test.cpp PROPERTIES COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" # see BuildVersion.cmake ) @@ -1631,7 +1637,7 @@ endif (WINDOWS) file(GLOB_RECURSE viewer_XUI_FILES LIST_DIRECTORIES FALSE ${CMAKE_CURRENT_SOURCE_DIR}/skins/*.xml) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/skins PREFIX "XUI Files" FILES ${viewer_XUI_FILES}) -set_source_files_properties(${viewer_XUI_FILES} +set_source_files_properties(${viewer_XUI_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) list(APPEND viewer_SOURCE_FILES ${viewer_XUI_FILES}) @@ -1639,7 +1645,7 @@ list(APPEND viewer_SOURCE_FILES ${viewer_XUI_FILES}) file(GLOB_RECURSE viewer_SHADER_FILES LIST_DIRECTORIES FALSE ${CMAKE_CURRENT_SOURCE_DIR}/app_settings/shaders/*.glsl) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/app_settings/shaders PREFIX "Shaders" FILES ${viewer_SHADER_FILES}) -set_source_files_properties(${viewer_SHADER_FILES} +set_source_files_properties(${viewer_SHADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) list(APPEND viewer_SOURCE_FILES ${viewer_SHADER_FILES}) @@ -1664,7 +1670,7 @@ set(viewer_APPSETTINGS_FILES app_settings/toolbars.xml app_settings/trees.xml app_settings/viewerart.xml - ${CMAKE_SOURCE_DIR}/../etc/message.xml + app_settings/message.xml ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg packages-info.txt featuretable.txt @@ -1758,7 +1764,7 @@ if (WINDOWS) set(COPY_INPUT_DEPENDENCIES # The following commented dependencies are determined at variably at build time. Can't do this here. - ${CMAKE_SOURCE_DIR}/../etc/message.xml + app_settings/message.xml ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg ${SHARED_LIB_STAGING_DIR}/openjp2.dll ${SHARED_LIB_STAGING_DIR}/llwebrtc.dll @@ -1966,7 +1972,7 @@ endif (WINDOWS) # one of these being libz where you can find four or more versions in play # at once. On Linux, libz can be found at link and run time via a number # of paths: -# +# # => -lfreetype # => libz.so.1 (on install machine, not build) # => -lSDL @@ -2042,7 +2048,7 @@ foreach(elem ${country_codes}) set(emoji_mapping_src_file "${emoji_mapping_src_folder}/${elem}/emoji_characters.xml") set(emoji_mapping_dst_file - "${emoji_mapping_dst_folder}/${elem}/emoji_characters.xml") + "${emoji_mapping_dst_folder}/${elem}/emoji_characters.xml") configure_file(${emoji_mapping_src_file} ${emoji_mapping_dst_file} COPYONLY) endforeach() @@ -2144,7 +2150,7 @@ if (DARWIN) # https://blog.kitware.com/upcoming-in-cmake-2-8-12-osx-rpath-support/ set(CMAKE_MACOSX_RPATH 1) - + set_target_properties( ${VIEWER_BINARY_NAME} PROPERTIES @@ -2187,9 +2193,6 @@ if (DARWIN) --grid=${GRID} --source=${CMAKE_CURRENT_SOURCE_DIR} --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt - DEPENDS - ${VIEWER_BINARY_NAME} - ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_libvlc media_plugin_cef) @@ -2224,8 +2227,6 @@ if (DARWIN) --touch=${CMAKE_CURRENT_BINARY_DIR}/$,$,>/.${product}.bat --versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt ${SIGNING_SETTING} - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) endif (PACKAGE) endif (DARWIN) @@ -2265,7 +2266,7 @@ if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIE PROPERTIES XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym" XCODE_ATTRIBUTE_DWARF_DSYM_FOLDER_PATH "${SYMBOLS_STAGING_DIR}/dSYMs") - + add_custom_command(OUTPUT "${VIEWER_APP_XCARCHIVE}" COMMAND "zip" ARGS @@ -2300,7 +2301,7 @@ if (LL_TESTS) # llremoteparcelrequest.cpp llviewerhelputil.cpp llversioninfo.cpp -# llvocache.cpp +# llvocache.cpp llworldmap.cpp llworldmipmap.cpp ) @@ -2309,7 +2310,7 @@ if (LL_TESTS) llworldmap.cpp llworldmipmap.cpp PROPERTIES - LL_TEST_ADDITIONAL_SOURCE_FILES + LL_TEST_ADDITIONAL_SOURCE_FILES tests/llviewertexture_stub.cpp #llviewertexturelist.cpp ) @@ -2343,7 +2344,7 @@ if (LL_TESTS) llworldmap.cpp llworldmipmap.cpp PROPERTIES - LL_TEST_ADDITIONAL_SOURCE_FILES + LL_TEST_ADDITIONAL_SOURCE_FILES tests/llviewertexture_stub.cpp ) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 0ee843cc60..b26a34e470 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.2.0 +7.2.1 diff --git a/etc/message.xml b/indra/newview/app_settings/message.xml similarity index 100% rename from etc/message.xml rename to indra/newview/app_settings/message.xml diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0614be8bf6..d64f82d303 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -102,7 +102,7 @@ Type S32 Value - 300.0 + 300 AckCollectTime @@ -1159,7 +1159,7 @@ Type Boolean Value - 0 + 1 ShowDiscordActivityDetails @@ -1908,6 +1908,17 @@ Value 0 + DebugSelectionLODs + + Comment + Force selection to show specific LOD, -1 for off, 0 - lowest, 4 - high. + Persist + 0 + Type + S32 + Value + -1 + AnimatedObjectsAllowLeftClick Comment @@ -14295,10 +14306,32 @@ Value 1 - OutfitGallerySortByName + OutfitGallerySortOrder Comment - Always sort outfits by name in Outfit Gallery + Gallery sorting: 0 - sort outfits by name, 1 - images frst, 2 - favorites first + Persist + 1 + Type + S32 + Value + 0 + + OutfitListSortOrder + + Comment + How outfit list in Avatar's floater is sorted. 0 - by name 1 - favorites to top + Persist + 1 + Type + S32 + Value + 0 + + OutfitListFilterFullList + + Comment + 0 - show only matches. 1 - show all items in outfit as long as outfit or item inside matches. Persist 1 Type @@ -16179,6 +16212,50 @@ Value 0 + InventoryFavoritesUseStar + + Comment + Show star near favorited items in inventory + Persist + 1 + Type + Boolean + Value + 1 + + InventoryFavoritesUseHollowStar + + Comment + Show star near folders that contain favorites + Persist + 1 + Type + Boolean + Value + 1 + + InventoryFavoritesColorText + + Comment + render favorite items using InventoryFavoriteText as color + Persist + 1 + Type + Boolean + Value + 1 + + InventoryAddAttachmentBehavior + + Comment + Defines behavior when hitting return on an inventory item + Persist + 1 + Type + Boolean + Value + 0 + StatsReportMaxDuration Comment diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 6b788dd78f..99c43acd49 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -329,7 +329,7 @@ KeepConversationLogTranscripts Comment - Keep a conversation log and transcripts + Keep a conversation log and transcripts 2 - both, 1 - logs, 0 - none Persist 1 Type diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index e24aea4a28..28f30ae1c9 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -589,7 +589,9 @@ bool Asset::prep() for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) { +#ifdef SHOW_ASSERT U32 attribute_mask = 0; +#endif // for each mesh for (auto& mesh : mMeshes) { @@ -607,7 +609,9 @@ bool Asset::prep() // all primitives of a given variant and material should all have the same attribute mask llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); +#ifdef SHOW_ASSERT attribute_mask |= primitive.mAttributeMask; +#endif } } } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 3cb5e9a0d7..ac452b38a0 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -220,6 +220,7 @@ void GLTFSceneManager::uploadSelection() LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), expected_upload_cost, + LLUUID::null, false, finish, failure)); @@ -283,6 +284,7 @@ void GLTFSceneManager::uploadSelection() LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), expected_upload_cost, + LLUUID::null, false, finish, failure)); @@ -559,6 +561,7 @@ void GLTFSceneManager::update() LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), expected_upload_cost, + LLUUID::null, false, finish, failure)); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 039bd4da2a..085155714a 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -121,8 +121,8 @@ const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds const S32 UI_FEATURE_VERSION = 1; -// For version 1: 1 - inventory, 2 - gltf -const S32 UI_FEATURE_FLAGS = 3; +// For version 1, flag holds: 1 - inventory thumbnails, 2 - gltf, 4 - inventory favorites +const S32 UI_FEATURE_FLAGS = 7; // The agent instance. LLAgent gAgent; @@ -223,7 +223,6 @@ private: LLVector3d mPosGlobal; }; - class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation { public: @@ -604,7 +603,7 @@ void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags) if (feature_version.isInteger()) { version = feature_version.asInteger(); - flags = 1; // inventory flag + flags = 3; // show 'favorites' notification } else if (feature_version.isMap()) { @@ -630,13 +629,8 @@ void LLAgent::showLatestFeatureNotification(const std::string key) if (key == "inventory") { - // Notify user about new thumbnail support - flag = 1; - } - - if (key == "gltf") - { - flag = 2; + // Notify user about new favorites support + flag = 4; } if ((flags & flag) == 0) @@ -843,7 +837,6 @@ void LLAgent::movePitch(F32 mag) } } - // Does this parcel allow you to fly? bool LLAgent::canFly() { @@ -923,7 +916,6 @@ void LLAgent::setFlying(bool fly, bool fail_sound) LLFloaterMove::setFlyingMode(fly); } - // UI based mechanism of setting fly state //----------------------------------------------------------------------------- // toggleFlying() @@ -1002,7 +994,6 @@ void LLAgent::capabilityReceivedCallback(const LLUUID ®ion_id, LLViewerRegion } } - //----------------------------------------------------------------------------- // setRegion() //----------------------------------------------------------------------------- @@ -1108,7 +1099,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp) mRegionChangedSignal(); } - //----------------------------------------------------------------------------- // getRegion() //----------------------------------------------------------------------------- @@ -1117,7 +1107,6 @@ LLViewerRegion *LLAgent::getRegion() const return mRegionp; } - LLHost LLAgent::getRegionHost() const { if (mRegionp) @@ -1148,7 +1137,6 @@ bool LLAgent::inPrelude() return mRegionp && mRegionp->isPrelude(); } - std::string LLAgent::getRegionCapability(const std::string &name) { if (!mRegionp) @@ -1157,7 +1145,6 @@ std::string LLAgent::getRegionCapability(const std::string &name) return mRegionp->getCapability(name); } - //----------------------------------------------------------------------------- // canManageEstate() //----------------------------------------------------------------------------- @@ -1185,7 +1172,6 @@ void LLAgent::sendMessage() gMessageSystem->sendMessage(mRegionp->getHost()); } - //----------------------------------------------------------------------------- // sendReliableMessage() //----------------------------------------------------------------------------- @@ -1219,7 +1205,6 @@ LLVector3 LLAgent::getVelocity() const } } - //----------------------------------------------------------------------------- // setPositionAgent() //----------------------------------------------------------------------------- @@ -1293,7 +1278,6 @@ const LLVector3 &LLAgent::getPositionAgent() } } - return mFrameAgent.getOrigin(); } @@ -1302,7 +1286,6 @@ boost::signals2::connection LLAgent::whenPositionChanged(position_signal_t::slot return mOnPositionChanged.connect(fn); } - //----------------------------------------------------------------------------- // getRegionsVisited() //----------------------------------------------------------------------------- @@ -1319,7 +1302,6 @@ F64 LLAgent::getDistanceTraveled() const return mDistanceTraveled; } - //----------------------------------------------------------------------------- // getPosAgentFromGlobal() //----------------------------------------------------------------------------- @@ -1330,7 +1312,6 @@ LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const return pos_agent; } - //----------------------------------------------------------------------------- // getPosGlobalFromAgent() //----------------------------------------------------------------------------- @@ -1346,7 +1327,6 @@ void LLAgent::sitDown() setControlFlags(AGENT_CONTROL_SIT_ON_GROUND); } - //----------------------------------------------------------------------------- // resetAxes() //----------------------------------------------------------------------------- @@ -1355,7 +1335,6 @@ void LLAgent::resetAxes() mFrameAgent.resetAxes(); } - // Copied from LLCamera::setOriginAndLookAt // Look_at must be unit vector //----------------------------------------------------------------------------- @@ -1384,7 +1363,6 @@ void LLAgent::resetAxes(const LLVector3 &look_at) mFrameAgent.setAxes(look_at, left, up); } - //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- @@ -1393,7 +1371,6 @@ void LLAgent::rotate(F32 angle, const LLVector3 &axis) mFrameAgent.rotate(angle, axis); } - //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- @@ -1402,7 +1379,6 @@ void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z) mFrameAgent.rotate(angle, x, y, z); } - //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- @@ -1411,7 +1387,6 @@ void LLAgent::rotate(const LLMatrix3 &matrix) mFrameAgent.rotate(matrix); } - //----------------------------------------------------------------------------- // rotate() //----------------------------------------------------------------------------- @@ -1420,7 +1395,6 @@ void LLAgent::rotate(const LLQuaternion &quaternion) mFrameAgent.rotate(quaternion); } - //----------------------------------------------------------------------------- // getReferenceUpVector() //----------------------------------------------------------------------------- @@ -1449,7 +1423,6 @@ LLVector3 LLAgent::getReferenceUpVector() return up_vector; } - // Radians, positive is forward into ground //----------------------------------------------------------------------------- // pitch() @@ -1493,7 +1466,6 @@ void LLAgent::pitch(F32 angle) } } - //----------------------------------------------------------------------------- // roll() //----------------------------------------------------------------------------- @@ -1502,7 +1474,6 @@ void LLAgent::roll(F32 angle) mFrameAgent.roll(angle); } - //----------------------------------------------------------------------------- // yaw() //----------------------------------------------------------------------------- @@ -1514,7 +1485,6 @@ void LLAgent::yaw(F32 angle) } } - // Returns a quat that represents the rotation of the agent in the absolute frame //----------------------------------------------------------------------------- // getQuat() @@ -1540,7 +1510,6 @@ void LLAgent::setControlFlags(U32 mask) mControlFlags |= mask; } - //----------------------------------------------------------------------------- // clearControlFlags() //----------------------------------------------------------------------------- @@ -1628,7 +1597,6 @@ bool LLAgent::isDoNotDisturb() const return mIsDoNotDisturb; } - //----------------------------------------------------------------------------- // startAutoPilotGlobal() //----------------------------------------------------------------------------- @@ -1734,7 +1702,6 @@ void LLAgent::startAutoPilotGlobal( mAutoPilotNoProgressFrameCount = 0; } - //----------------------------------------------------------------------------- // setAutoPilotTargetGlobal //----------------------------------------------------------------------------- @@ -1788,7 +1755,6 @@ void LLAgent::startFollowPilot(const LLUUID &leader_id, bool allow_flying, F32 s allow_flying); } - //----------------------------------------------------------------------------- // stopAutoPilot() //----------------------------------------------------------------------------- @@ -1830,7 +1796,6 @@ void LLAgent::stopAutoPilot(bool user_cancel) } } - // Returns necessary agent pitch and yaw changes, radians. //----------------------------------------------------------------------------- // autoPilot() @@ -2019,7 +1984,6 @@ void LLAgent::autoPilot(F32 *delta_yaw) } } - //----------------------------------------------------------------------------- // propagate() //----------------------------------------------------------------------------- @@ -2040,18 +2004,19 @@ void LLAgent::propagate(const F32 dt) } // handle rotation based on keyboard levels - constexpr F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second - F32 angle = YAW_RATE * gAgentCamera.getYawKey() * dt; - if (fabs(angle) > 0.0f) + if (fabs(dt) > 1e-6) { - yaw(angle); - } + if (fabs(gAgentCamera.getYawKey()) > 1e-6) + { + static const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second + yaw(YAW_RATE * gAgentCamera.getYawKey() * dt); + } - constexpr F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second - angle = PITCH_RATE * gAgentCamera.getPitchKey() * dt; - if (fabs(angle) > 0.0f) - { - pitch(angle); + if (fabs(gAgentCamera.getPitchKey()) > 1e-6) + { + static const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second + pitch(PITCH_RATE * gAgentCamera.getPitchKey() * dt); + } } // handle auto-land behavior @@ -2213,7 +2178,6 @@ void LLAgent::clearRenderState(U8 clearstate) mRenderState &= ~clearstate; } - //----------------------------------------------------------------------------- // getRenderState() //----------------------------------------------------------------------------- @@ -2255,6 +2219,7 @@ void LLAgent::endAnimationUpdateUI() { return; } + if (gAgentCamera.getCameraMode() == gAgentCamera.getLastCameraMode()) { // We're already done endAnimationUpdateUI for this transition. @@ -2320,7 +2285,6 @@ void LLAgent::endAnimationUpdateUI() mViewsPushed = false; } - gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); if( gMorphView ) { @@ -2952,7 +2916,6 @@ void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity) } } - void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity) { U8 maturity = SIM_ACCESS_MIN; @@ -3022,7 +2985,6 @@ void LLAgent::changeInterestListMode(const std::string &new_mode) } } - bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure) { if (getRegion()) @@ -3349,7 +3311,6 @@ void LLAgent::sendAnimationStateReset() sendReliableMessage(); } - // Send a message to the region to revoke sepecified permissions on ALL scripts in the region // If the target is an object in the region, permissions in scripts on that object are cleared. // If it is the region ID, all scripts clear the permissions for this agent @@ -3469,14 +3430,11 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global) bool LLAgent::leftButtonGrabbed() const { - if (gAgentCamera.cameraMouselook()) - { - return mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0; - } - else - { - return mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0; - } + const bool camera_mouse_look = gAgentCamera.cameraMouselook(); + return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) + || (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0) + || (!camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) + || (camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0); } bool LLAgent::rotateGrabbed() const @@ -4282,7 +4240,6 @@ void LLAgent::onCapabilitiesReceivedAfterTeleport() check_merchant_status(); } - void LLAgent::teleportRequest( const U64& region_handle, const LLVector3& pos_local, @@ -4396,7 +4353,6 @@ void LLAgent::doTeleportViaLure(const LLUUID& lure_id, bool godlike) } } - // James Cook, July 28, 2005 void LLAgent::teleportCancel() { @@ -4521,7 +4477,6 @@ LLAgent::ETeleportState LLAgent::getTeleportState() const TELEPORT_NONE : mTeleportState; } - void LLAgent::setTeleportState(ETeleportState state) { if (mTeleportRequest && (state != TELEPORT_NONE) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed)) @@ -4566,7 +4521,6 @@ void LLAgent::setTeleportState(ETeleportState state) } } - void LLAgent::stopCurrentAnimations() { LL_DEBUGS("Avatar") << "Stopping current animations" << LL_ENDL; @@ -4681,7 +4635,6 @@ void LLAgent::stopFidget() gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP); } - void LLAgent::requestEnterGodMode() { LLMessageSystem* msg = gMessageSystem; @@ -4802,7 +4755,6 @@ void LLAgent::sendAgentUpdateUserInfo(const std::string& directory_visibility) } } - void LLAgent::updateAgentUserInfoCoro(std::string capurl, std::string directory_visibility) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index d4767e18af..b2c66b1bac 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -1462,13 +1462,12 @@ void LLAgentCamera::updateCamera() // LL_INFOS() << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << LL_ENDL; LLVector3 focus_agent = gAgent.getPosAgentFromGlobal(mFocusGlobal); + LLVector3 position_agent = gAgent.getPosAgentFromGlobal(camera_pos_global); - mCameraPositionAgent = gAgent.getPosAgentFromGlobal(camera_pos_global); + // Try to move the camera - // Move the camera - - LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent); - //LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent); + if (!LLViewerCamera::getInstance()->updateCameraLocation(position_agent, mCameraUpVector, focus_agent)) + return; // Change FOV LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / (1.f + mCameraCurrentFOVZoomFactor)); @@ -1476,7 +1475,7 @@ void LLAgentCamera::updateCamera() // follow camera when in customize mode if (cameraCustomizeAvatar()) { - setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent); + setLookAt(LOOKAT_TARGET_FOCUS, NULL, position_agent); } // update the travel distance stat @@ -1495,8 +1494,8 @@ void LLAgentCamera::updateCamera() LLVector3 head_pos = gAgentAvatarp->mHeadp->getWorldPosition() + LLVector3(0.08f, 0.f, 0.05f) * gAgentAvatarp->mHeadp->getWorldRotation() + LLVector3(0.1f, 0.f, 0.f) * gAgentAvatarp->mPelvisp->getWorldRotation(); - LLVector3 diff = mCameraPositionAgent - head_pos; - diff = diff * ~gAgentAvatarp->mRoot->getWorldRotation(); + LLVector3 diff = position_agent - head_pos; + diff *= ~gAgentAvatarp->mRoot->getWorldRotation(); LLJoint* torso_joint = gAgentAvatarp->mTorsop; LLJoint* chest_joint = gAgentAvatarp->mChestp; @@ -1753,7 +1752,6 @@ F32 LLAgentCamera::calcCameraFOVZoomFactor() LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit) { // Compute base camera position and look-at points. - F32 camera_land_height; LLVector3d frame_center_global = !isAgentAvatarValid() ? gAgent.getPositionGlobal() : gAgent.getPosGlobalFromAgent(getAvatarRootPosition()); @@ -1990,10 +1988,11 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit) } } - // Don't let camera go underground - F32 camera_min_off_ground = getCameraMinOffGround(); - camera_land_height = LLWorld::getInstance()->resolveLandHeightGlobal(camera_position_global); - F32 minZ = llmax(F_ALMOST_ZERO, camera_land_height + camera_min_off_ground); + // Don't let camera go underground if constrained + // If not constrained, permit going 1000m below 0, use case: retrieving objects + F32 camera_min_off_ground = getCameraMinOffGround(); // checks isDisableCameraConstraints + F32 camera_land_height = LLWorld::getInstance()->resolveLandHeightGlobal(camera_position_global); + F32 minZ = camera_land_height + camera_min_off_ground; if (camera_position_global.mdV[VZ] < minZ) { camera_position_global.mdV[VZ] = minZ; @@ -2256,7 +2255,8 @@ void LLAgentCamera::changeCameraToFollow(bool animate) mCameraMode = CAMERA_MODE_FOLLOW; // bang-in the current focus, position, and up vector of the follow cam - mFollowCam.reset(mCameraPositionAgent, LLViewerCamera::getInstance()->getPointOfInterest(), LLVector3::z_axis); + const LLViewerCamera& camera = LLViewerCamera::instance(); + mFollowCam.reset(camera.getOrigin(), camera.getPointOfInterest(), LLVector3::z_axis); if (gBasicToolset) { diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h index 52571f3c55..d277fd6158 100644 --- a/indra/newview/llagentcamera.h +++ b/indra/newview/llagentcamera.h @@ -112,6 +112,7 @@ private: //-------------------------------------------------------------------- public: void switchCameraPreset(ECameraPreset preset); + ECameraPreset getCameraPreset() const { return mCameraPreset; } /** Determines default camera offset depending on the current camera preset */ LLVector3 getCameraOffsetInitial(); /** Determines default focus offset depending on the current camera preset */ @@ -138,13 +139,14 @@ private: //-------------------------------------------------------------------- public: LLVector3d getCameraPositionGlobal() const; - const LLVector3 &getCameraPositionAgent() const; + const LLVector3& getCameraPositionAgent() const; LLVector3d calcCameraPositionTargetGlobal(bool *hit_limit = NULL); // Calculate the camera position target F32 getCameraMinOffGround(); // Minimum height off ground for this mode, meters void setCameraCollidePlane(const LLVector4 &plane) { mCameraCollidePlane = plane; } bool calcCameraMinDistance(F32 &obj_min_distance); - F32 getCurrentCameraBuildOffset() { return (F32)mCameraFocusOffset.length(); } + F32 getCurrentCameraBuildOffset() const { return (F32)mCameraFocusOffset.length(); } void clearCameraLag() { mCameraLag.clearVec(); } + const LLVector3& getCameraUpVector() const { return mCameraUpVector; } private: LLVector3 getAvatarRootPosition(); @@ -154,7 +156,6 @@ private: F32 mCameraCurrentFOVZoomFactor; // Interpolated fov zoom LLVector4 mCameraCollidePlane; // Colliding plane for camera F32 mCameraZoomFraction; // Mousewheel driven fraction of zoom - LLVector3 mCameraPositionAgent; // Camera position in agent coordinates LLVector3 mCameraVirtualPositionAgent; // Camera virtual position (target) before performing FOV zoom LLVector3d mCameraSmoothingLastPositionGlobal; LLVector3d mCameraSmoothingLastPositionAgent; @@ -278,7 +279,7 @@ public: F32 getAgentHUDTargetZoom(); void resetCameraZoomFraction(); - F32 getCurrentCameraZoomFraction() { return mCameraZoomFraction; } + F32 getCurrentCameraZoomFraction() const { return mCameraZoomFraction; } //-------------------------------------------------------------------- // Pan diff --git a/indra/newview/llagentpilot.cpp b/indra/newview/llagentpilot.cpp index 0b5198bbd3..8b18b7d5a2 100644 --- a/indra/newview/llagentpilot.cpp +++ b/indra/newview/llagentpilot.cpp @@ -322,9 +322,7 @@ void LLAgentPilot::moveCamera() LLViewerCamera::getInstance()->setView(view); LLViewerCamera::getInstance()->setOrigin(origin); - LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]); - LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]); - LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]); + LLViewerCamera::getInstance()->setAxes(mat); } } diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 11c5ffecb6..5114ac8a08 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -839,7 +839,7 @@ void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& if ( (type == UPDATECATEGORY || type == UPDATEITEM) && gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_ais_update", update); } AISUpdate ais_update(update, type, request_body); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 8a17ccfeef..6fa23727b1 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2045,7 +2045,7 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) } // Moved from LLWearableList::ContextMenu for wider utility. -bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch) const { // TODO: investigate wearables may not be loaded at this point EXT-8231 @@ -2075,7 +2075,10 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const } else { + if (warn_on_type_mismatch) + { LL_WARNS() << "Unexpected wearable type" << LL_ENDL; + } return false; } } @@ -2266,7 +2269,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) } if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_slam_request", contents); } slam_inventory_folder(getCOF(), contents, link_waiter); @@ -3959,7 +3962,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", result); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_ok", result); } } while (bRetry); @@ -3968,7 +3971,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd /*static*/ void LLAppearanceMgr::debugAppearanceUpdateCOF(const LLSD& content) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_error", content); LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger() << " ================================= " << LL_ENDL; diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index bc7dc9506b..0a41a91750 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -103,7 +103,7 @@ public: bool getCanReplaceCOF(const LLUUID& outfit_cat_id); // Can we add all referenced items to the avatar? - bool canAddWearables(const uuid_vec_t& item_ids) const; + bool canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch = true) const; // Copy all items in a category. void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f7168598dc..7a1154a992 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -453,13 +453,28 @@ static bool app_metrics_qa_mode = false; void idle_afk_check() { + // Don't check AFK status during startup states + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return; + } + // check idle timers F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32(); static LLCachedControl afk_timeout(gSavedSettings, "AFKTimeout", 300); - if (afk_timeout() && (current_idle > (F32)afk_timeout()) && !gAgent.getAFK()) + if (afk_timeout() && (current_idle > afk_timeout())) { - LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; - gAgent.setAFK(); + if (!gAgent.getAFK()) + { + LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; + gAgent.setAFK(); + } + else + { + // Refresh timer so that random one click or hover won't clear the status. + // But expanding the window still should lift afk status + gAwayTimer.reset(); + } } } @@ -1852,36 +1867,6 @@ bool LLAppViewer::cleanup() // Clean up before GL is shut down because we might be holding on to objects with texture references LLSelectMgr::cleanupGlobals(); - LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; - - // Shut down OpenGL - if( gViewerWindow) - { - gViewerWindow->shutdownGL(); - - // Destroy window, and make sure we're not fullscreen - // This may generate window reshape and activation events. - // Therefore must do this before destroying the message system. - delete gViewerWindow; - gViewerWindow = NULL; - LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; - } - - LLSplashScreen::show(); - LLSplashScreen::update(LLTrans::getString("ShuttingDown")); - - LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; - - // viewer UI relies on keyboard so keep it aound until viewer UI isa gone - delete gKeyboard; - gKeyboard = NULL; - - if (LLViewerJoystick::instanceExists()) - { - // Turn off Space Navigator and similar devices - LLViewerJoystick::getInstance()->terminate(); - } - LL_INFOS() << "Cleaning up Objects" << LL_ENDL; LLViewerObject::cleanupVOClasses(); @@ -2042,6 +2027,36 @@ bool LLAppViewer::cleanup() sTextureFetch->shutDownTextureCacheThread() ; LLLFSThread::sLocal->shutdown(); + LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; + + // Shut down OpenGL + if (gViewerWindow) + { + gViewerWindow->shutdownGL(); + + // Destroy window, and make sure we're not fullscreen + // This may generate window reshape and activation events. + // Therefore must do this before destroying the message system. + delete gViewerWindow; + gViewerWindow = NULL; + LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; + } + + LLSplashScreen::show(); + LLSplashScreen::update(LLTrans::getString("ShuttingDown")); + + LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; + + // viewer UI relies on keyboard so keep it aound until viewer UI isa gone + delete gKeyboard; + gKeyboard = NULL; + + if (LLViewerJoystick::instanceExists()) + { + // Turn off Space Navigator and similar devices + LLViewerJoystick::getInstance()->terminate(); + } + LL_INFOS() << "Shutting down message system" << LL_ENDL; end_messaging_system(); @@ -4527,6 +4542,7 @@ void LLAppViewer::forceDisconnect(const std::string& mesg) } else { + sendSimpleLogoutRequest(); args["MESSAGE"] = big_reason; LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect ); } @@ -5307,6 +5323,27 @@ void LLAppViewer::sendLogoutRequest() } } +void LLAppViewer::sendSimpleLogoutRequest() +{ + if (!mLogoutRequestSent && gMessageSystem) + { + gLogoutInProgress = true; + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LogoutRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + + LL_INFOS("Agent") << "Logging out as agent: " << gAgent.getID() << " Session: " << gAgent.getSessionID() << LL_ENDL; + + gLogoutTimer.reset(); + gLogoutMaxTime = LOGOUT_REQUEST_TIME; + mLogoutRequestSent = true; + } +} + void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp) { if (!regionp || !regionp->capabilitiesReceived()) @@ -5926,100 +5963,21 @@ void LLAppViewer::initDiscordSocial() gDiscordPartyMaxSize = 0; gDiscordTimestampsStart = time(nullptr); gDiscordClient = std::make_shared(); - gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { - if (status == discordpp::Client::Status::Ready) - { - updateDiscordActivity(); - } - }); - if (gSavedSettings.getBOOL("EnableDiscord")) - { - auto credential = gSecAPIHandler->loadCredential("Discord"); - if (credential.notNull()) - { - gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { - if (result.Successful()) - gDiscordClient->Connect(); - else - LL_WARNS("Discord") << result.Error() << LL_ENDL; - }); - } - else - { - LL_WARNS("Discord") << "Integration was enabled, but no credentials. Disabling integration." << LL_ENDL; - gSavedSettings.setBOOL("EnableDiscord", false); - } - } -} - -void LLAppViewer::toggleDiscordIntegration(const LLSD& value) -{ - static const uint64_t APPLICATION_ID = 1394782217405862001; - if (value.asBoolean()) - { - discordpp::AuthorizationArgs args{}; - args.SetClientId(APPLICATION_ID); - args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); - auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); - args.SetCodeChallenge(codeVerifier.Challenge()); - gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) { - if (result.Successful()) - { - gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { - if (result.Successful()) - { - gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [accessToken](discordpp::ClientResult result) { - if (result.Successful()) - { - LLSD authenticator = LLSD::emptyMap(); - authenticator["token"] = accessToken; - gSecAPIHandler->saveCredential(gSecAPIHandler->createCredential("Discord", LLSD::emptyMap(), authenticator), true); - gDiscordClient->Connect(); - } - else - { - LL_WARNS("Discord") << result.Error() << LL_ENDL; - } - }); - } - else - { - LL_WARNS("Discord") << result.Error() << LL_ENDL; - } - }); - } - else - { - LL_WARNS("Discord") << result.Error() << LL_ENDL; - gSavedSettings.setBOOL("EnableDiscord", false); - } - }); - } - else - { - gDiscordClient->Disconnect(); - auto credential = gSecAPIHandler->loadCredential("Discord"); - if (credential.notNull()) - { - gDiscordClient->RevokeToken(APPLICATION_ID, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { - if (result.Successful()) - LL_INFOS("Discord") << "Access token successfully revoked." << LL_ENDL; - else - LL_WARNS("Discord") << "No access token to revoke." << LL_ENDL; - }); - auto cred = new LLCredential("Discord"); - gSecAPIHandler->deleteCredential(cred); - } - else - { - LL_WARNS("Discord") << "Credentials are already nonexistent." << LL_ENDL; - } - } + gDiscordClient->SetApplicationId(1394782217405862001); + updateDiscordActivity(); } void LLAppViewer::updateDiscordActivity() { LL_PROFILE_ZONE_SCOPED; + + static LLCachedControl integration_enabled(gSavedSettings, "EnableDiscord", true); + if (!integration_enabled) + { + gDiscordClient->ClearRichPresence(); + return; + } + discordpp::Activity activity; activity.SetType(discordpp::ActivityTypes::Playing); discordpp::ActivityTimestamps timestamps; @@ -6047,37 +6005,39 @@ void LLAppViewer::updateDiscordActivity() activity.SetDetails(gDiscordActivityDetails); } + auto agent_pos_region = gAgent.getPositionAgent(); + S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); + S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); + S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); + F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); + const F32 FLY_CUTOFF = 6.f; + const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; + const F32 WALK_CUTOFF = 1.5f; + const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; + if (velocity_mag_sq > FLY_CUTOFF_SQ) + { + pos_x -= pos_x % 4; + pos_y -= pos_y % 4; + } + else if (velocity_mag_sq > WALK_CUTOFF_SQ) + { + pos_x -= pos_x % 2; + pos_y -= pos_y % 2; + } + + std::string location = "Hidden Region"; static LLCachedControl show_state(gSavedSettings, "ShowDiscordActivityState", false); if (show_state) { - auto agent_pos_region = gAgent.getPositionAgent(); - S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); - S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); - S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); - F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); - const F32 FLY_CUTOFF = 6.f; - const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; - const F32 WALK_CUTOFF = 1.5f; - const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; - if (velocity_mag_sq > FLY_CUTOFF_SQ) - { - pos_x -= pos_x % 4; - pos_y -= pos_y % 4; - } - else if (velocity_mag_sq > WALK_CUTOFF_SQ) - { - pos_x -= pos_x % 2; - pos_y -= pos_y % 2; - } - auto location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z); - activity.SetState(location); - - discordpp::ActivityParty party; - party.SetId(location); - party.SetCurrentSize(gDiscordPartyCurrentSize); - party.SetMaxSize(gDiscordPartyMaxSize); - activity.SetParty(party); + location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z); } + activity.SetState(location); + + discordpp::ActivityParty party; + party.SetId(location); + party.SetCurrentSize(gDiscordPartyCurrentSize); + party.SetMaxSize(gDiscordPartyMaxSize); + activity.SetParty(party); gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 918300f6e5..3afde6b9f5 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -254,7 +254,6 @@ public: #ifdef LL_DISCORD static void initDiscordSocial(); - static void toggleDiscordIntegration(const LLSD& value); static void updateDiscordActivity(); static void updateDiscordPartyCurrentSize(int32_t size); static void updateDiscordPartyMaxSize(int32_t size); @@ -312,6 +311,10 @@ private: void sendLogoutRequest(); void disconnectViewer(); + // Does not create a marker file. For lost network case, + // to at least attempt to remove the ghost from the world. + void sendSimpleLogoutRequest(); + // *FIX: the app viewer class should be some sort of singleton, no? // Perhaps its child class is the singleton and this should be an abstract base. static LLAppViewer* sInstance; diff --git a/indra/newview/llautoreplace.cpp b/indra/newview/llautoreplace.cpp index f200ca8e31..1ea2899ba4 100644 --- a/indra/newview/llautoreplace.cpp +++ b/indra/newview/llautoreplace.cpp @@ -536,11 +536,12 @@ LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::replaceList(const LL S32 search_index; LLSD targetList; // The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30 - for ( search_index = 0, targetList = mLists[0]; + for ( search_index = 0; !listFound && search_index < mLists.size(); - search_index += 1, targetList = mLists[search_index] + search_index += 1 ) { + targetList = mLists[search_index]; if ( targetList.isMap() ) { if ( listNameMatches( targetList, listName) ) diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index a48e22bc73..1988e2072b 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -189,7 +189,14 @@ public: std::string url = "secondlife://" + mObjectData["slurl"].asString(); LLUrlAction::teleportToLocation(url); } - + else if (level == "obj_zoom_in") + { + LLUUID obj_id = mObjectData["object_id"]; + if (obj_id.notNull()) + { + handle_zoom_to_object(obj_id); + } + } } bool onObjectIconContextMenuItemVisible(const LLSD& userdata) @@ -203,6 +210,15 @@ public: { return !LLMuteList::getInstance()->isMuted(getAvatarId(), mFrom, LLMute::flagTextChat); } + else if (level == "obj_zoom_in") + { + LLUUID obj_id = mObjectData["object_id"]; + if (obj_id.notNull()) + { + return nullptr != gObjectList.findObject(mAvatarID); + } + return false; + } return false; } @@ -936,7 +952,7 @@ protected: menu->setItemEnabled("Voice Call", false); menu->setItemEnabled("Chat History", false); menu->setItemEnabled("Invite Group", false); - menu->setItemEnabled("Zoom In", false); + menu->setItemEnabled("Zoom In", true); menu->setItemEnabled("Share", false); menu->setItemEnabled("Pay", false); menu->setItemEnabled("Block Unblock", false); diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 550dfeb802..5ac4ce0d52 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -37,6 +37,8 @@ #include "lllocalcliprect.h" #include "lltrans.h" #include "llfloaterimnearbychat.h" +#include "llfloaterworldmap.h" +#include "llviewermenu.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -75,6 +77,23 @@ public: return true; } + if (verb == "zoomin") + { + if (!handle_zoom_to_object(object_id) && params.size() > 2) + { + // zoom faled, show location + // secondlife:///app/object/object_id/zoomin/{LOCATION}/{COORDS} SLapp + const std::string region_name = LLURI::unescape(params[0].asString()); + S32 x = (params.size() > 1) ? params[1].asInteger() : 128; + S32 y = (params.size() > 2) ? params[2].asInteger() : 128; + S32 z = (params.size() > 3) ? params[3].asInteger() : 0; + + LLFloaterWorldMap::getInstance()->trackURL(region_name, x, y, z); + LLFloaterReg::showInstance("world_map", "center"); + } + return true; + } + return false; } }; diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp index 9201241856..660fb1b41a 100644 --- a/indra/newview/llcontrolavatar.cpp +++ b/indra/newview/llcontrolavatar.cpp @@ -129,14 +129,14 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_ { LLVector3 pos_box_offset = point_to_box_offset(vol_pos, unshift_extents); F32 offset_dist = pos_box_offset.length(); - if (offset_dist > MAX_LEGAL_OFFSET && offset_dist > 0.f) + if (offset_dist > max_legal_offset && offset_dist > 0.f) { - F32 target_dist = (offset_dist - MAX_LEGAL_OFFSET); + F32 target_dist = (offset_dist - max_legal_offset); new_pos_fixup = (target_dist/offset_dist)*pos_box_offset; } if (new_pos_fixup != mPositionConstraintFixup) { - LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup " + LL_DEBUGS("ConstraintFix") << getDebugName() << " pos fix, offset_dist " << offset_dist << " pos fixup " << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL; LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL; LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL; @@ -144,11 +144,11 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_ } } - if (box_size/mScaleConstraintFixup > MAX_LEGAL_SIZE) + if (box_size/mScaleConstraintFixup > max_legal_size) { - new_scale_fixup = mScaleConstraintFixup* MAX_LEGAL_SIZE /box_size; - LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup " - << mScaleConstraintFixup << " max legal " << MAX_LEGAL_SIZE + new_scale_fixup = mScaleConstraintFixup*max_legal_size/box_size; + LL_DEBUGS("ConstraintFix") << getDebugName() << " scale fix, box_size " << box_size << " fixup " + << mScaleConstraintFixup << " max legal " << max_legal_size << " -> new scale " << new_scale_fixup << LL_ENDL; } } @@ -231,7 +231,7 @@ void LLControlAvatar::matchVolumeTransform() const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo(); if (skin_info) { - LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL; + LL_DEBUGS("BindShape") << getDebugName() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL; bind_rot = LLSkinningUtil::getUnscaledQuaternion(LLMatrix4(skin_info->mBindShapeMatrix)); } #endif diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index e6a720e734..cea68c1779 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -663,7 +663,7 @@ void LLConversationLog::onClearLogResponse(const LLSD& notification, const LLSD& { mConversations.clear(); notifyObservers(); - cache(); + saveToFile(getFileName()); deleteBackupLogs(); } } diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 5004055666..bb1daf4ec1 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -300,7 +300,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic for (auto itemp : mChildren) { - LLConversationItem* current_participant = dynamic_cast(itemp); + LLConversationItem* current_participant = dynamic_cast(itemp.get()); // Add the avatar uuid to the list (except if it's the own agent uuid) if (current_participant->getUUID() != gAgentID) { @@ -329,6 +329,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant) { + LLPointer holder(participant); removeChild(participant); mNeedsRefresh = true; updateName(participant); @@ -360,15 +361,10 @@ void LLConversationItemSession::clearAndDeparentModels() for (child_list_t::iterator it = mChildren.begin(); it != mChildren.end();) { LLFolderViewModelItem* child = *it; - if (child->getNumRefs() == 0) + // Note that model might still be assigned to some view/widget + // and have a different parent + if (child->getParent() == this) { - // LLConversationItemParticipant can be created but not assigned to any view, - // it was waiting for an "add_participant" event to be processed - delete child; - } - else - { - // Model is still assigned to some view/widget child->setParent(NULL); } it = mChildren.erase(it); @@ -383,7 +379,7 @@ LLConversationItemParticipant* LLConversationItemSession::findParticipant(const child_list_t::iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) { - participant = dynamic_cast(*iter); + participant = dynamic_cast((*iter).get()); if (participant && participant->hasSameValue(participant_id)) { break; @@ -493,7 +489,7 @@ const bool LLConversationItemSession::getTime(F64& time) const child_list_t::const_iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) { - participant = dynamic_cast(*iter); + participant = dynamic_cast((*iter).get()); F64 participant_time; if (participant && participant->getTime(participant_time)) { @@ -517,7 +513,7 @@ void LLConversationItemSession::dumpDebugData(bool dump_children) { for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++) { - LLConversationItemParticipant* participant = dynamic_cast(*iter); + LLConversationItemParticipant* participant = dynamic_cast((*iter).get()); if (participant) { participant->dumpDebugData(); diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index c1e48c63a9..d5486b9f4a 100644 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -79,6 +79,9 @@ public: virtual LLPointer getOpenIcon() const { return getIcon(); } virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } + virtual bool isFavorite() const { return false; } + virtual bool isAgentInventory() const { return false; } + virtual bool isAgentInventoryRoot() const { return false; } virtual bool isItemRenameable() const { return true; } virtual bool renameItem(const std::string& new_name) { mName = new_name; mNeedsRefresh = true; return true; } virtual bool isItemMovable( void ) const { return false; } diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index 7d58511d41..cdf3244389 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -143,7 +143,6 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass) gGL.setColorMask(true, true); LLColor3 light_diffuse(0, 0, 0); - F32 light_exp = 0.0f; LLEnvironment& environment = LLEnvironment::instance(); LLSettingsWater::ptr_t pwater = environment.getCurrentWater(); @@ -170,7 +169,6 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass) // Apply magic numbers translating light direction into intensities light_dir.normalize(); F32 ground_proj_sq = light_dir.mV[0] * light_dir.mV[0] + light_dir.mV[1] * light_dir.mV[1]; - light_exp = llmax(32.f, 256.f * powf(ground_proj_sq, 16.0f)); if (0.f < light_diffuse.normalize()) // Normalizing a color? Puzzling... { light_diffuse *= (1.5f + (6.f * ground_proj_sq)); diff --git a/indra/newview/llfloaterautoreplacesettings.cpp b/indra/newview/llfloaterautoreplacesettings.cpp index 99f24e161e..d93bd624f5 100644 --- a/indra/newview/llfloaterautoreplacesettings.cpp +++ b/indra/newview/llfloaterautoreplacesettings.cpp @@ -422,7 +422,13 @@ bool LLFloaterAutoReplaceSettings::callbackNewListName(const LLSD& notification, LLSD newList = notification["payload"]["list"]; - if ( response.has("listname") && response["listname"].isString() ) + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 1) // Must also match RenameAutoReplaceList + { + // user cancelled + return false; + } + else if (response.has("listname") && response["listname"].isString() ) { std::string newName = response["listname"].asString(); LLAutoReplaceSettings::setListName(newList, newName); @@ -508,12 +514,53 @@ bool LLFloaterAutoReplaceSettings::callbackListNameConflict(const LLSD& notifica return false; } +bool LLFloaterAutoReplaceSettings::callbackRemoveList(const LLSD& notification, const LLSD& response) +{ + std::string listName = notification["payload"]["list"]; + + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch (option) + { + case 1: + if (mSettings.removeReplacementList(listName)) + { + LL_INFOS("AutoReplace") << "deleted list '" << listName << "'" << LL_ENDL; + mReplacementsList->deleteSelectedItems(); // remove from the scrolling list + mSelectedListName.clear(); + updateListNames(); + updateListNamesControls(); + updateReplacementsList(); + } + break; + case 0: + break; + + default: + LL_ERRS("AutoReplace") << "invalid selected option " << option << LL_ENDL; + } + + return false; +} + void LLFloaterAutoReplaceSettings::onDeleteList() { std::string listName = mListNames->getSelectedValue().asString(); if ( ! listName.empty() ) { - if ( mSettings.removeReplacementList(listName) ) + const LLSD* mappings = mSettings.getListEntries(mSelectedListName); + if (mappings->size() > 0) + { + LLSD payload; + payload["list"] = listName; + + LLSD args; + args["MAP_SIZE"] = llformat("%d",mappings->size()); + args["LIST_NAME"] = listName; + + LLNotificationsUtil::add("RemoveAutoReplaceList", args, payload, + boost::bind(&LLFloaterAutoReplaceSettings::callbackRemoveList, this, _1, _2)); + } + else if ( mSettings.removeReplacementList(listName) ) { LL_INFOS("AutoReplace")<<"deleted list '"<deleteSelectedItems(); // remove from the scrolling list diff --git a/indra/newview/llfloaterautoreplacesettings.h b/indra/newview/llfloaterautoreplacesettings.h index 94a7c00c15..1a8068ab7c 100644 --- a/indra/newview/llfloaterautoreplacesettings.h +++ b/indra/newview/llfloaterautoreplacesettings.h @@ -105,6 +105,8 @@ private: bool callbackNewListName(const LLSD& notification, const LLSD& response); /// called from the RenameAutoReplaceList notification dialog bool callbackListNameConflict(const LLSD& notification, const LLSD& response); + /// called from the RemoveAutoReplaceList notification dialog + bool callbackRemoveList(const LLSD& notification, const LLSD& response); bool selectedListIsFirst(); bool selectedListIsLast(); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 5ee93be061..392079efe4 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -118,8 +118,8 @@ std::string STATUS[] = //----------------------------------------------------------------------------- // LLFloaterBvhPreview() //----------------------------------------------------------------------------- -LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename) : - LLFloaterNameDesc(filename) +LLFloaterBvhPreview::LLFloaterBvhPreview(const LLSD& args) : + LLFloaterNameDesc(args) { mLastMouseX = 0; mLastMouseY = 0; @@ -1028,7 +1028,8 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLFloaterPerms::getNextOwnerPerms("Uploads"), LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost)); + expected_upload_cost, + floaterp->mDestinationFolderId)); upload_new_resource(assetUploadInfo); } diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h index bb69ab65ef..1eb7f686fd 100644 --- a/indra/newview/llfloaterbvhpreview.h +++ b/indra/newview/llfloaterbvhpreview.h @@ -70,7 +70,7 @@ protected: class LLFloaterBvhPreview : public LLFloaterNameDesc { public: - LLFloaterBvhPreview(const std::string& filename); + LLFloaterBvhPreview(const LLSD& args); virtual ~LLFloaterBvhPreview(); bool postBuild(); diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp index 1cfed122a9..a3eb286375 100644 --- a/indra/newview/llfloaterchatmentionpicker.cpp +++ b/indra/newview/llfloaterchatmentionpicker.cpp @@ -88,7 +88,7 @@ uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds() LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); while (current_participant_model != end_participant_model) { - LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { avatar_ids.push_back(participant_model->getUUID()); diff --git a/indra/newview/llfloatereditenvironmentbase.h b/indra/newview/llfloatereditenvironmentbase.h index 37fda5d33e..41192f3d30 100644 --- a/indra/newview/llfloatereditenvironmentbase.h +++ b/indra/newview/llfloatereditenvironmentbase.h @@ -133,7 +133,8 @@ protected: LLSettingsEditPanel() : LLPanel(), mIsDirty(false), - mOnDirtyChanged() + mOnDirtyChanged(), + mCanEdit(false) {} private: diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 49e557a6cb..44e71e33f3 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -78,8 +78,8 @@ const S32 PREVIEW_TEXTURE_HEIGHT = 320; //----------------------------------------------------------------------------- // LLFloaterImagePreview() //----------------------------------------------------------------------------- -LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename) : - LLFloaterNameDesc(filename), +LLFloaterImagePreview::LLFloaterImagePreview(const LLSD& args) : + LLFloaterNameDesc(args), mAvatarPreview(NULL), mSculptedPreview(NULL), @@ -288,7 +288,9 @@ void LLFloaterImagePreview::onBtnOK() } else { - LLNotificationsUtil::add("ErrorEncodingImage"); + LLSD args; + args["REASON"] = LLImage::getLastThreadError(); + LLNotificationsUtil::add("ErrorEncodingImage", args); LL_WARNS() << "Error encoding image" << LL_ENDL; } } @@ -423,6 +425,18 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename) return false; } + // raw image is limited to 256MB so need at least some upper limit that fits into that + constexpr S32 MAX_IMAGE_AREA = 8096 * 8096; + + if (image_info.getWidth() * image_info.getHeight() > MAX_IMAGE_AREA) + { + LLStringUtil::format_map_t args; + args["PIXELS"] = llformat("%dM", (S32)(MAX_IMAGE_AREA / 1000000)); + + mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args); + return false; + } + // Load the image LLPointer image = LLImageFormatted::createFromType(codec); if (image.isNull()) diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h index 0ebb96a768..5e5f4932c2 100644 --- a/indra/newview/llfloaterimagepreview.h +++ b/indra/newview/llfloaterimagepreview.h @@ -110,7 +110,7 @@ protected: class LLFloaterImagePreview : public LLFloaterNameDesc { public: - LLFloaterImagePreview(const std::string& filename); + LLFloaterImagePreview(const LLSD& args); virtual ~LLFloaterImagePreview(); bool postBuild() override; diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 72d4d30dcf..d821d9a4a5 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -460,7 +460,7 @@ void LLFloaterIMContainer::processParticipantsStyleUpdate() LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { // Get the avatar name for this participant id from the cache and update the model @@ -511,7 +511,7 @@ void LLFloaterIMContainer::idleUpdate() bool can_ban = haveAbilityToBan(); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { participant_model->setModeratorOptionsVisible(is_moderator); @@ -1540,6 +1540,10 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v // Beyond that point, if only the user agent is selected, everything is disabled if (is_single_select && (single_id == gAgentID)) { + if ("can_zoom_in" == item) + { + return true; + } if (is_moderator_option) { return enableModerateContextMenuItem(item, true); @@ -1874,7 +1878,7 @@ LLConversationItem* LLFloaterIMContainer::addConversationListItem(const LLUUID& LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); while (current_participant_model != end_participant_model) { - LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); participant_view->addToFolder(widget); current_participant_model++; diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 7c3d2b511b..e03422780a 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -107,26 +107,6 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab() delete mRefreshTimer; LLIMMgr::instance().removeSessionObserver(this); mEmojiCloseConn.disconnect(); - - LLFloaterIMContainer* im_container = LLFloaterIMContainer::findInstance(); - if (im_container) - { - LLParticipantList* session = dynamic_cast(im_container->getSessionModel(mSessionID)); - if (session) - { - for (const conversations_widgets_map::value_type& widget_pair : mConversationsWidgets) - { - LLFolderViewItem* widget = widget_pair.second; - LLFolderViewModelItem* item_vmi = widget->getViewModelItem(); - if (item_vmi && item_vmi->getNumRefs() == 1) - { - // This is the last pointer, remove participant from session - // before participant gets deleted on destroyView. - session->removeChild(item_vmi); - } - } - } - } } // static @@ -730,7 +710,7 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant() LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); while (current_participant_model != end_participant_model) { - LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + LLConversationItem* participant_model = dynamic_cast((*current_participant_model).get()); if (participant_model) { addConversationViewParticipant(participant_model); @@ -774,27 +754,6 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); if (widget) { - LLFolderViewModelItem* item_vmi = widget->getViewModelItem(); - if (item_vmi && item_vmi->getNumRefs() == 1) - { - // This is the last pointer, remove participant from session - // before participant gets deleted on destroyView. - // - // Floater (widget) and participant's view can simultaneously - // co-own the model, in which case view is responsible for - // the deletion and floater is free to clear and recreate - // the list, yet there are cases where only widget owns - // the pointer so it should do the cleanup. - // See "add_participant". - // - // Todo: If it keeps causing issues turn participants - // into LLPointers in the session - LLParticipantList* session = getParticipantList(); - if (session) - { - session->removeChild(item_vmi); - } - } widget->destroyView(); } mConversationsWidgets.erase(participant_id); @@ -860,7 +819,7 @@ void LLFloaterIMSessionTab::refreshConversation() LLIMSpeakerMgr *speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); while (current_participant_model != end_participant_model) { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + LLConversationItemParticipant* participant_model = dynamic_cast((*current_participant_model).get()); if (speaker_mgr && participant_model) { LLSpeaker *participant_speaker = speaker_mgr->findSpeaker(participant_model->getUUID()); diff --git a/indra/newview/llfloaterinventorysettings.cpp b/indra/newview/llfloaterinventorysettings.cpp index e5ee69f240..aab632bcb8 100644 --- a/indra/newview/llfloaterinventorysettings.cpp +++ b/indra/newview/llfloaterinventorysettings.cpp @@ -28,9 +28,14 @@ #include "llfloaterinventorysettings.h" +#include "llcolorswatch.h" +#include "llviewercontrol.h" + LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key) : LLFloater(key) { + mCommitCallbackRegistrar.add("ScriptPref.applyUIColor", boost::bind(&LLFloaterInventorySettings::applyUIColor, this, _1, _2)); + mCommitCallbackRegistrar.add("ScriptPref.getUIColor", boost::bind(&LLFloaterInventorySettings::getUIColor, this, _1, _2)); } LLFloaterInventorySettings::~LLFloaterInventorySettings() @@ -39,6 +44,29 @@ LLFloaterInventorySettings::~LLFloaterInventorySettings() bool LLFloaterInventorySettings::postBuild() { getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloater::closeFloater, this, false)); + + getChild("favorites_color")->setCommitCallback(boost::bind(&LLFloaterInventorySettings::updateColorSwatch, this)); + + bool enable_color = gSavedSettings.getBOOL("InventoryFavoritesColorText"); + getChild("favorites_swatch")->setEnabled(enable_color); + return true; } +void LLFloaterInventorySettings::updateColorSwatch() +{ + bool val = getChild("favorites_color")->getValue(); + getChild("favorites_swatch")->setEnabled(val); +} + +void LLFloaterInventorySettings::applyUIColor(LLUICtrl* ctrl, const LLSD& param) +{ + LLUIColorTable::instance().setColor(param.asString(), LLColor4(ctrl->getValue())); +} + +void LLFloaterInventorySettings::getUIColor(LLUICtrl* ctrl, const LLSD& param) +{ + LLColorSwatchCtrl* color_swatch = (LLColorSwatchCtrl*)ctrl; + color_swatch->setOriginal(LLUIColorTable::instance().getColor(param.asString())); +} + diff --git a/indra/newview/llfloaterinventorysettings.h b/indra/newview/llfloaterinventorysettings.h index 3fe3a001b9..c27d5d2e1b 100644 --- a/indra/newview/llfloaterinventorysettings.h +++ b/indra/newview/llfloaterinventorysettings.h @@ -40,6 +40,11 @@ public: private: LLFloaterInventorySettings(const LLSD& key); ~LLFloaterInventorySettings(); + + void updateColorSwatch(); + + void applyUIColor(LLUICtrl* ctrl, const LLSD& param); + void getUIColor(LLUICtrl* ctrl, const LLSD& param); }; #endif diff --git a/indra/newview/llfloatermarketplace.cpp b/indra/newview/llfloatermarketplace.cpp new file mode 100644 index 0000000000..889daf84ab --- /dev/null +++ b/indra/newview/llfloatermarketplace.cpp @@ -0,0 +1,47 @@ +/** + * @file llfloatermarketplace.cpp + * @brief floater for the Marketplace web site + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatermarketplace.h" +#include "lluictrlfactory.h" + +LLFloaterMarketplace::LLFloaterMarketplace(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterMarketplace::~LLFloaterMarketplace() +{ +} + +bool LLFloaterMarketplace::postBuild() +{ + enableResizeCtrls(true, true, false); + return true; +} + + diff --git a/indra/newview/llfloatermarketplace.h b/indra/newview/llfloatermarketplace.h new file mode 100644 index 0000000000..2ae4d0d64a --- /dev/null +++ b/indra/newview/llfloatermarketplace.h @@ -0,0 +1,40 @@ +/** + * @file llfloatermarketplace.h + * @brief floater for the Marketplace web site + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "llfloater.h" + +class LLFloaterMarketplace: + public LLFloater +{ + friend class LLFloaterReg; +private: + LLFloaterMarketplace(const LLSD& key); + ~LLFloaterMarketplace(); + bool postBuild() override; +}; + diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 332a98031f..6a6766fb3f 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -165,7 +165,7 @@ bool LLFloaterModelPreview::postBuild() for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) { LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[lod]); - lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod, true)); + lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod)); lod_source_combo->setCurrentByIndex(mLODMode[lod]); getChild("lod_browse_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onBrowseLOD, this, lod)); @@ -350,14 +350,14 @@ void LLFloaterModelPreview::initModelPreview() } //static -bool LLFloaterModelPreview::showModelPreview() +void LLFloaterModelPreview::showModelPreview(const LLUUID& dest_folder) { LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)LLFloaterReg::getInstance("upload_model"); if (fmp && !fmp->isModelLoading()) { + fmp->setUploadDestination(dest_folder); fmp->loadHighLodModel(); } - return true; } void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl) @@ -506,7 +506,7 @@ void LLFloaterModelPreview::onClickCalculateBtn() gMeshRepo.uploadModel(mModelPreview->mUploadData, mModelPreview->mPreviewScale, childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, - mUploadModelUrl, false, + mUploadModelUrl, mDestinationFolderId, false, getWholeModelFeeObserverHandle()); toggleCalculateButton(false); @@ -766,7 +766,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) LLComboBox* lod_source_combo = getChild("lod_source_" + lod_name[i]); if (lod_source_combo->getCurrentIndex() == LLModelPreview::USE_LOD_ABOVE) { - onLoDSourceCommit(i, false); + onLoDSourceCommit(i); } else { @@ -1659,7 +1659,7 @@ void LLFloaterModelPreview::onUpload(void* user_data) gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, mp->childGetValue("upload_textures").asBoolean(), upload_skinweights, upload_joint_positions, lock_scale_if_joint_position, - mp->mUploadModelUrl, + mp->mUploadModelUrl, mp->mDestinationFolderId, true, LLHandle(), mp->getWholeModelUploadObserverHandle()); } @@ -1760,7 +1760,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible) } } -void LLFloaterModelPreview::onLoDSourceCommit(S32 lod, bool refresh_ui) +void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) { mModelPreview->updateLodControls(lod); @@ -1773,12 +1773,10 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod, bool refresh_ui) // rebuild LoD to update triangle counts onLODParamCommit(lod, true); } - else if (refresh_ui && index == LLModelPreview::USE_LOD_ABOVE) + if (index == LLModelPreview::USE_LOD_ABOVE) { - // Update mUploadData for updateStatusMessages - mModelPreview->rebuildUploadData(); - // Update UI with new triangle values - mModelPreview->updateStatusMessages(); + // refresh to pick triangle counts + mModelPreview->mDirty = true; } } diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 5d23dc8d8e..7b652a3613 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -73,7 +73,8 @@ public: /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); void initModelPreview(); - static bool showModelPreview(); + void setUploadDestination(const LLUUID& dest_folder) { mDestinationFolderId = dest_folder; } + static void showModelPreview(const LLUUID& dest_folder = LLUUID::null); bool handleMouseDown(S32 x, S32 y, MASK mask); bool handleMouseUp(S32 x, S32 y, MASK mask); @@ -164,9 +165,6 @@ protected: static void onPhysicsBrowse(LLUICtrl* ctrl, void* userdata); static void onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata); - static void onPhysicsOptimize(LLUICtrl* ctrl, void* userdata); - static void onPhysicsDecomposeBack(LLUICtrl* ctrl, void* userdata); - static void onPhysicsSimplifyBack(LLUICtrl* ctrl, void* userdata); void draw(); @@ -208,7 +206,7 @@ private: void onClickCalculateBtn(); void onJointListSelection(); - void onLoDSourceCommit(S32 lod, bool refresh_ui); + void onLoDSourceCommit(S32 lod); void modelUpdated(bool calculate_visible); @@ -225,6 +223,7 @@ private: void createSmoothComboBox(LLComboBox* combo_box, float min, float max); + LLUUID mDestinationFolderId; LLButton* mUploadBtn; LLButton* mCalculateBtn; LLViewerTextEditor* mUploadLogText; diff --git a/indra/newview/llfloatermyenvironment.cpp b/indra/newview/llfloatermyenvironment.cpp index 891e16a8ef..c0405c106e 100644 --- a/indra/newview/llfloatermyenvironment.cpp +++ b/indra/newview/llfloatermyenvironment.cpp @@ -38,7 +38,9 @@ #include "llcheckboxctrl.h" #include "llviewerinventory.h" #include "llenvironment.h" +#include "llnotificationsutil.h" #include "llparcel.h" +#include "lltrans.h" #include "llviewerparcelmgr.h" //========================================================================= @@ -223,16 +225,13 @@ void LLFloaterMyEnvironment::onFilterEdit(const std::string& search_string) mInventoryList->setFilterSubString(search_string); } -void LLFloaterMyEnvironment::onDeleteSelected() +void LLFloaterMyEnvironment::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, uuid_vec_t item_ids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) { - uuid_vec_t selected; - - getSelectedIds(selected); - if (selected.empty()) - return; - const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - for (const LLUUID& itemid: selected) + for (const LLUUID& itemid : item_ids) { LLInventoryItem* inv_item = gInventory.getItem(itemid); @@ -253,6 +252,27 @@ void LLFloaterMyEnvironment::onDeleteSelected() } gInventory.notifyObservers(); } +} + +void LLFloaterMyEnvironment::onDeleteSelected() +{ + uuid_vec_t selected; + + getSelectedIds(selected); + if (selected.empty()) + return; + + LLSD args; + args["QUESTION"] = LLTrans::getString(selected.size() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add( + "DeleteItems", + args, + LLSD(), + [this, selected](const LLSD& notification, const LLSD& response) + { + onItemsRemovalConfirmation(notification, response, selected); + }); +} void LLFloaterMyEnvironment::onDoCreate(const LLSD &data) @@ -318,13 +338,13 @@ bool LLFloaterMyEnvironment::canAction(const std::string &context) if (context == PARAMETER_EDIT) { - return (selected.size() == 1) && isSettingSelected(selected.front()); + return (selected.size() == 1) && isSettingId(selected.front()); } else if (context == PARAMETER_COPY) { for (std::vector::iterator it = selected.begin(); it != selected.end(); it++) { - if(!isSettingSelected(*it)) + if(!isSettingId(*it)) { return false; } @@ -342,7 +362,7 @@ bool LLFloaterMyEnvironment::canAction(const std::string &context) LLClipboard::instance().pasteFromClipboard(ids); for (std::vector::iterator it = ids.begin(); it != ids.end(); it++) { - if (!isSettingSelected(*it)) + if (!isSettingId(*it)) { return false; } @@ -351,7 +371,7 @@ bool LLFloaterMyEnvironment::canAction(const std::string &context) } else if (context == PARAMETER_COPYUUID) { - return (selected.size() == 1) && isSettingSelected(selected.front()); + return (selected.size() == 1) && isSettingId(selected.front()); } return false; @@ -367,16 +387,42 @@ bool LLFloaterMyEnvironment::canApply(const std::string &context) if (context == PARAMETER_REGION) { - return LLEnvironment::instance().canAgentUpdateRegionEnvironment(); + return isSettingId(selected.front()) && LLEnvironment::instance().canAgentUpdateRegionEnvironment(); } else if (context == PARAMETER_PARCEL) { - return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); + return isSettingId(selected.front()) && LLEnvironment::instance().canAgentUpdateParcelEnvironment(); } - else + else if (context == PARAMETER_LOCAL) { - return (context == PARAMETER_LOCAL); + return isSettingId(selected.front()); } + + return false; +} + +bool can_delete(const LLUUID& id) +{ + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (id == trash_id || gInventory.isObjectDescendentOf(id, trash_id)) + { + return false; + } + + LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if (cat) + { + if (!get_is_category_removable(&gInventory, id)) + { + return false; + } + } + else if (!get_is_item_removable(&gInventory, id, false)) + { + return false; + } + + return true; } //------------------------------------------------------------------------- @@ -389,7 +435,14 @@ void LLFloaterMyEnvironment::refreshButtonStates() getChild(BUTTON_GEAR)->setEnabled(settings_ok); getChild(BUTTON_NEWSETTINGS)->setEnabled(true); - getChild(BUTTON_DELETE)->setEnabled(settings_ok && !selected.empty()); + + bool enable_delete = false; + if(settings_ok && !selected.empty()) + { + enable_delete = can_delete(selected.front()); + } + + getChild(BUTTON_DELETE)->setEnabled(enable_delete); } //------------------------------------------------------------------------- @@ -438,7 +491,7 @@ LLUUID LLFloaterMyEnvironment::findItemByAssetId(LLUUID asset_id, bool copyable_ return LLUUID::null; } -bool LLFloaterMyEnvironment::isSettingSelected(LLUUID item_id) +bool LLFloaterMyEnvironment::isSettingId(const LLUUID& item_id) { LLInventoryItem* itemp = gInventory.getItem(item_id); diff --git a/indra/newview/llfloatermyenvironment.h b/indra/newview/llfloatermyenvironment.h index 8e81b8e5e2..c5d521d207 100644 --- a/indra/newview/llfloatermyenvironment.h +++ b/indra/newview/llfloatermyenvironment.h @@ -60,6 +60,7 @@ private: void onFilterCheckChange(); void onFilterEdit(const std::string& search_string); void onSelectionChange(); + void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, uuid_vec_t item_ids); void onDeleteSelected(); void onDoCreate(const LLSD &data); void onDoApply(const std::string &context); @@ -69,7 +70,7 @@ private: void getSelectedIds(uuid_vec_t& ids) const; void refreshButtonStates(); - bool isSettingSelected(LLUUID item_id); + static bool isSettingId(const LLUUID &item_id); static LLUUID findItemByAssetId(LLUUID asset_id, bool copyable_only, bool ignore_library); }; diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index 01c50d89c5..569b41cfa9 100644 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -62,11 +62,20 @@ const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; //----------------------------------------------------------------------------- // LLFloaterNameDesc() //----------------------------------------------------------------------------- -LLFloaterNameDesc::LLFloaterNameDesc(const LLSD& filename ) - : LLFloater(filename), - mIsAudio(false) +LLFloaterNameDesc::LLFloaterNameDesc(const LLSD& args) + : LLFloater(args) + , mIsAudio(false) + , mIsText(false) { - mFilenameAndPath = filename.asString(); + if (args.isString()) + { + mFilenameAndPath = args.asString(); + } + else + { + mFilenameAndPath = args["filename"].asString(); + mDestinationFolderId = args["dest"].asUUID(); + } mFilename = gDirUtilp->getBaseFileName(mFilenameAndPath, false); } @@ -203,7 +212,8 @@ void LLFloaterNameDesc::onBtnOK( ) LLFloaterPerms::getNextOwnerPerms("Uploads"), LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost)); + expected_upload_cost, + mDestinationFolderId)); upload_new_resource(uploadInfo, callback, nruserdata); } @@ -230,8 +240,8 @@ void LLFloaterNameDesc::onBtnCancel() // LLFloaterSoundPreview() //----------------------------------------------------------------------------- -LLFloaterSoundPreview::LLFloaterSoundPreview(const LLSD& filename ) - : LLFloaterNameDesc(filename) +LLFloaterSoundPreview::LLFloaterSoundPreview(const LLSD& args ) + : LLFloaterNameDesc(args) { mIsAudio = true; } @@ -251,8 +261,8 @@ bool LLFloaterSoundPreview::postBuild() // LLFloaterAnimPreview() //----------------------------------------------------------------------------- -LLFloaterAnimPreview::LLFloaterAnimPreview(const LLSD& filename ) - : LLFloaterNameDesc(filename) +LLFloaterAnimPreview::LLFloaterAnimPreview(const LLSD& args ) + : LLFloaterNameDesc(args) { } @@ -270,8 +280,8 @@ bool LLFloaterAnimPreview::postBuild() // LLFloaterScriptPreview() //----------------------------------------------------------------------------- -LLFloaterScriptPreview::LLFloaterScriptPreview(const LLSD& filename ) - : LLFloaterNameDesc(filename) +LLFloaterScriptPreview::LLFloaterScriptPreview(const LLSD& args ) + : LLFloaterNameDesc(args) { mIsText = true; } diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h index aa5571ccc0..8c8ec49a8e 100644 --- a/indra/newview/llfloaternamedesc.h +++ b/indra/newview/llfloaternamedesc.h @@ -39,7 +39,7 @@ class LLRadioGroup; class LLFloaterNameDesc : public LLFloater { public: - LLFloaterNameDesc(const LLSD& filename); + LLFloaterNameDesc(const LLSD& args); virtual ~LLFloaterNameDesc(); bool postBuild() override; @@ -58,6 +58,7 @@ protected: std::string mFilenameAndPath; std::string mFilename; + LLUUID mDestinationFolderId; }; class LLFloaterSoundPreview : public LLFloaterNameDesc diff --git a/indra/newview/llfloaterobjectweights.cpp b/indra/newview/llfloaterobjectweights.cpp index 26b7304b9a..fa491a4b27 100644 --- a/indra/newview/llfloaterobjectweights.cpp +++ b/indra/newview/llfloaterobjectweights.cpp @@ -36,6 +36,14 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" +static const std::string lod_strings[4] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", +}; + // virtual bool LLCrossParcelFunctor::apply(LLViewerObject* obj) { @@ -75,7 +83,10 @@ LLFloaterObjectWeights::LLFloaterObjectWeights(const LLSD& key) mSelectedOnLand(NULL), mRezzedOnLand(NULL), mRemainingCapacity(NULL), - mTotalCapacity(NULL) + mTotalCapacity(NULL), + mLodLevel(nullptr), + mTrianglesShown(nullptr), + mPixelArea(nullptr) { } @@ -99,6 +110,10 @@ bool LLFloaterObjectWeights::postBuild() mRemainingCapacity = getChild("remaining_capacity"); mTotalCapacity = getChild("total_capacity"); + mLodLevel = getChild("lod_level"); + mTrianglesShown = getChild("triangles_shown"); + mPixelArea = getChild("pixel_area"); + return true; } @@ -135,6 +150,69 @@ void LLFloaterObjectWeights::setErrorStatus(S32 status, const std::string& reaso toggleWeightsLoadingIndicators(false); } +void LLFloaterObjectWeights::draw() +{ + // Normally it's a bad idea to set text and visibility inside draw + // since it can cause rect updates go to different, already drawn elements, + // but floater is very simple and these elements are supposed to be isolated + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + if (selection->isEmpty()) + { + const std::string text = getString("nothing_selected"); + mLodLevel->setText(text); + mTrianglesShown->setText(text); + mPixelArea->setText(text); + + toggleRenderLoadingIndicators(false); + } + else + { + S32 object_lod = -1; + bool multiple_lods = false; + S32 total_tris = 0; + F32 pixel_area = 0; + for (LLObjectSelection::valid_root_iterator iter = selection->valid_root_begin(); + iter != selection->valid_root_end(); ++iter) + { + LLViewerObject* object = (*iter)->getObject(); + S32 lod = object->getLOD(); + if (object_lod < 0) + { + object_lod = lod; + } + else if (object_lod != lod) + { + multiple_lods = true; + } + + if (object->isRootEdit()) + { + total_tris += object->recursiveGetTriangleCount(); + pixel_area += object->getPixelArea(); + } + } + + if (multiple_lods) + { + mLodLevel->setText(getString("multiple_lods")); + toggleRenderLoadingIndicators(false); + } + else if (object_lod < 0) + { + // nodes are waiting for data + toggleRenderLoadingIndicators(true); + } + else + { + mLodLevel->setText(getString(lod_strings[object_lod])); + toggleRenderLoadingIndicators(false); + } + mTrianglesShown->setText(llformat("%d", total_tris)); + mPixelArea->setText(llformat("%d", pixel_area)); + } + LLFloater::draw(); +} + void LLFloaterObjectWeights::updateLandImpacts(const LLParcel* parcel) { if (!parcel || LLSelectMgr::getInstance()->getSelection()->isEmpty()) @@ -252,6 +330,17 @@ void LLFloaterObjectWeights::toggleLandImpactsLoadingIndicators(bool visible) mTotalCapacity->setVisible(!visible); } +void LLFloaterObjectWeights::toggleRenderLoadingIndicators(bool visible) +{ + childSetVisible("lod_level_loading_indicator", visible); + childSetVisible("triangles_shown_loading_indicator", visible); + childSetVisible("pixel_area_loading_indicator", visible); + + mLodLevel->setVisible(!visible); + mTrianglesShown->setVisible(!visible); + mPixelArea->setVisible(!visible); +} + void LLFloaterObjectWeights::updateIfNothingSelected() { const std::string text = getString("nothing_selected"); @@ -269,6 +358,11 @@ void LLFloaterObjectWeights::updateIfNothingSelected() mRemainingCapacity->setText(text); mTotalCapacity->setText(text); + mLodLevel->setText(text); + mTrianglesShown->setText(text); + mPixelArea->setText(text); + toggleWeightsLoadingIndicators(false); toggleLandImpactsLoadingIndicators(false); + toggleRenderLoadingIndicators(false); } diff --git a/indra/newview/llfloaterobjectweights.h b/indra/newview/llfloaterobjectweights.h index 3b999f6b9b..bda625564b 100644 --- a/indra/newview/llfloaterobjectweights.h +++ b/indra/newview/llfloaterobjectweights.h @@ -58,21 +58,24 @@ public: LLFloaterObjectWeights(const LLSD& key); ~LLFloaterObjectWeights(); - /*virtual*/ bool postBuild(); + bool postBuild() override; - /*virtual*/ void onOpen(const LLSD& key); + void onOpen(const LLSD& key) override; - /*virtual*/ void onWeightsUpdate(const SelectionCost& selection_cost); - /*virtual*/ void setErrorStatus(S32 status, const std::string& reason); + void onWeightsUpdate(const SelectionCost& selection_cost) override; + void setErrorStatus(S32 status, const std::string& reason) override; + + void draw() override; void updateLandImpacts(const LLParcel* parcel); - void refresh(); + void refresh() override; private: - /*virtual*/ void generateTransactionID(); + void generateTransactionID() override; void toggleWeightsLoadingIndicators(bool visible); void toggleLandImpactsLoadingIndicators(bool visible); + void toggleRenderLoadingIndicators(bool visible); void updateIfNothingSelected(); @@ -88,6 +91,10 @@ private: LLTextBox *mRezzedOnLand; LLTextBox *mRemainingCapacity; LLTextBox *mTotalCapacity; + + LLTextBox *mLodLevel; + LLTextBox *mTrianglesShown; + LLTextBox *mPixelArea; }; #endif //LL_LLFLOATEROBJECTWEIGHTS_H diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index c84728248c..b74736a39d 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -367,7 +367,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this)); mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // Hook up for filtering #ifdef LL_DISCORD - gSavedSettings.getControl("EnableDiscord")->getCommitSignal()->connect(boost::bind(&LLAppViewer::toggleDiscordIntegration, _2)); + gSavedSettings.getControl("EnableDiscord")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity)); gSavedSettings.getControl("ShowDiscordActivityDetails")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity)); gSavedSettings.getControl("ShowDiscordActivityState")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity)); #endif @@ -529,7 +529,8 @@ bool LLFloaterPreference::postBuild() } #ifndef LL_DISCORD - getChild("privacy_tab_container")->childDisable("privacy_preferences_discord"); + LLPanel* panel = getChild("privacy_preferences_discord"); + getChild("privacy_tab_container")->removeTabPanel(panel); #endif return true; @@ -1977,7 +1978,21 @@ void LLFloaterPreference::selectChatPanel() void LLFloaterPreference::changed() { + if (LLConversationLog::instance().getIsLoggingEnabled()) + { getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); + } + else + { + // onClearLog clears list, then notifies changed() and only then clears file, + // so check presence of conversations before checking file, file will cleared later. + llstat st; + bool has_logs = LLConversationLog::instance().getConversations().size() > 0 + && LLFile::stat(LLConversationLog::instance().getFileName(), &st) == 0 + && S_ISREG(st.st_mode) + && st.st_size > 0; + getChild("clear_log")->setEnabled(has_logs); + } // set 'enable' property for 'Delete transcripts...' button updateDeleteTranscriptsButton(); diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index d3c8bf3451..9762154a26 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -1,11 +1,10 @@ /** * @file llfloatersearch.cpp - * @author Martin Reddy - * @brief Search floater - uses an embedded web browser control + * @brief Floater for Search (update 2025, preload) * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2011, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,68 +26,34 @@ #include "llviewerprecompiledheaders.h" +#include "llfloatersearch.h" + +#include "llagent.h" #include "llcommandhandler.h" #include "llfloaterreg.h" -#include "llfloatersearch.h" -#include "llhttpconstants.h" #include "llmediactrl.h" -#include "llnotificationsutil.h" -#include "lllogininstance.h" -#include "lluri.h" -#include "llagent.h" -#include "llui.h" +#include "lluictrlfactory.h" #include "llviewercontrol.h" #include "llweb.h" // support secondlife:///app/search/{CATEGORY}/{QUERY} SLapps -class LLSearchHandler : public LLCommandHandler -{ -public: - // requires trusted browser to trigger - LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_CLICK_ONLY) { } - bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) - { - const size_t parts = tokens.size(); +class LLSearchHandler : public LLCommandHandler { + public: + // requires trusted browser to trigger + LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_CLICK_ONLY) { } + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { + const size_t parts = tokens.size(); - // get the (optional) category for the search - std::string collection; - if (parts > 0) - { - collection = tokens[0].asString(); + // open the search floater and perform the requested search + LLFloaterReg::showInstance("search", tokens); + return true; } - - // get the (optional) search string - std::string search_text; - if (parts > 1) - { - search_text = tokens[1].asString(); - } - - // create the LLSD arguments for the search floater - LLFloaterSearch::Params p; - p.search.collection = collection; - p.search.query = LLURI::unescape(search_text); - - // open the search floater and perform the requested search - LLFloaterReg::showInstance("search", p); - return true; - } }; LLSearchHandler gSearchHandler; -LLFloaterSearch::SearchQuery::SearchQuery() -: category("category", ""), - collection("collection", ""), - query("query") -{} - -LLFloaterSearch::LLFloaterSearch(const Params& key) : - LLFloaterWebContent(key), - mSearchGodLevel(0) +LLFloaterSearch::LLFloaterSearch(const LLSD& key) + : LLFloater(key) { - // declare a map that transforms a category name into - // the URL suffix that is used to search that category - mSearchType.insert("standard"); mSearchType.insert("land"); mSearchType.insert("classified"); @@ -100,76 +65,61 @@ LLFloaterSearch::LLFloaterSearch(const Params& key) : mCollectionType.insert("people"); } -bool LLFloaterSearch::postBuild() +LLFloaterSearch::~LLFloaterSearch() { - LLFloaterWebContent::postBuild(); - mWebBrowser->addObserver(this); - - return true; } -void LLFloaterSearch::onOpen(const LLSD& key) +void LLFloaterSearch::onOpen(const LLSD& tokens) { - Params p(key); - p.trusted_content = true; - p.allow_address_entry = false; - - LLFloaterWebContent::onOpen(p); - mWebBrowser->setFocus(true); - search(p.search); + initiateSearch(tokens); } -void LLFloaterSearch::onClose(bool app_quitting) +void LLFloaterSearch::initiateSearch(const LLSD& tokens) { - LLFloaterWebContent::onClose(app_quitting); - // tear down the web view so we don't show the previous search - // result when the floater is opened next time - destroy(); -} + std::string url = gSavedSettings.getString("SearchURL"); -void LLFloaterSearch::godLevelChanged(U8 godlevel) -{ - // search results can change based upon god level - if the user - // changes god level, then give them a warning (we don't refresh - // the search as this might undo any page navigation or - // AJAX-driven changes since the last search). + LLSD subs; - //FIXME: set status bar text + // Setting this substitution here results in a full set of collections being + // substituted into the final URL using the logic from the original search. + subs["TYPE"] = "standard"; - //getChildView("refresh_search")->setVisible( (godlevel != mSearchGodLevel)); -} + const size_t parts = tokens.size(); -void LLFloaterSearch::search(const SearchQuery &p) -{ - if (! mWebBrowser || !p.validateBlock()) + // get the (optional) category for the search + std::string collection; + if (parts > 0) { - return; + collection = tokens[0].asString(); } - // reset the god level warning as we're sending the latest state - getChildView("refresh_search")->setVisible(false); - mSearchGodLevel = gAgent.getGodLevel(); - - // work out the subdir to use based on the requested category - LLSD subs; - if (mSearchType.find(p.category) != mSearchType.end()) + // get the (optional) search string + std::string search_text; + if (parts > 1) { - subs["TYPE"] = p.category; + search_text = tokens[1].asString(); + } + + // TODO: where does category get set? I cannot find a reference to + // it in internal docs - might be conflated with values in mSearchType + std::string category; + if (mSearchType.find(category) != mSearchType.end()) + { + subs["TYPE"] = category; } else { subs["TYPE"] = "standard"; } - // add the search query string - subs["QUERY"] = LLURI::escape(p.query); + subs["QUERY"] = LLURI::escape(search_text); subs["COLLECTION"] = ""; if (subs["TYPE"] == "standard") { - if (mCollectionType.find(p.collection) != mCollectionType.end()) + if (mCollectionType.find(collection) != mCollectionType.end()) { - subs["COLLECTION"] = "&collection_chosen=" + std::string(p.collection); + subs["COLLECTION"] = "&collection_chosen=" + std::string(collection); } else { @@ -182,30 +132,41 @@ void LLFloaterSearch::search(const SearchQuery &p) } } - // add the user's preferred maturity (can be changed via prefs) - std::string maturity; + // Default to PG + std::string maturity = "g"; if (gAgent.prefersAdult()) { - maturity = "gma"; // PG,Mature,Adult + // PG,Mature,Adult + maturity = "gma"; } else if (gAgent.prefersMature()) { - maturity = "gm"; // PG,Mature - } - else - { - maturity = "g"; // PG + // PG,Mature + maturity = "gm"; } subs["MATURITY"] = maturity; - // add the user's god status + // God status subs["GODLIKE"] = gAgent.isGodlike() ? "1" : "0"; - // get the search URL and expand all of the substitutions - // (also adds things like [LANGUAGE], [VERSION], [OS], etc.) - std::string url = gSavedSettings.getString("SearchURL"); + // This call expands a set of generic substitutions like language, viewer version + // etc. and then also does the same with the list of subs passed in. url = LLWeb::expandURLSubstitutions(url, subs); - // and load the URL in the web view - mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + // Naviation to the calculated URL - we know it's HTML so we can + // tell the media system not to bother with the MIME type check. + LLMediaCtrl* search_browser = findChild("search_contents"); + search_browser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); +} + +bool LLFloaterSearch::postBuild() +{ + enableResizeCtrls(true, true, false); + + // This call is actioned by the preload code in llViewerWindow + // that creates the search floater during the login process + // using a generic search with no query + initiateSearch(LLSD()); + + return true; } diff --git a/indra/newview/llfloatersearch.h b/indra/newview/llfloatersearch.h index beaac2ad2f..e8a2be4797 100644 --- a/indra/newview/llfloatersearch.h +++ b/indra/newview/llfloatersearch.h @@ -1,11 +1,10 @@ /** * @file llfloatersearch.h - * @author Martin Reddy - * @brief Search floater - uses an embedded web browser control + * @brief Floater for Search (update 2025, preload) * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2011, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,70 +24,23 @@ * $/LicenseInfo$ */ -#ifndef LL_LLFLOATERSEARCH_H -#define LL_LLFLOATERSEARCH_H +#pragma once -#include "llfloaterwebcontent.h" -#include "llviewermediaobserver.h" +#include "llfloater.h" -#include +class LLFloaterSearch: + public LLFloater { + friend class LLFloaterReg; -class LLMediaCtrl; + public: + void onOpen(const LLSD& key) override; -/// -/// The search floater allows users to perform all search operations. -/// All search functionality is now implemented via web services and -/// so this floater simply embeds a web view and displays the search -/// web page. The browser control is explicitly marked as "trusted" -/// so that the user can click on teleport links in search results. -/// -class LLFloaterSearch : - public LLFloaterWebContent -{ -public: - struct SearchQuery : public LLInitParam::Block - { - Optional category; - Optional collection; - Optional query; + private: + LLFloaterSearch(const LLSD& key); + ~LLFloaterSearch(); + void initiateSearch(const LLSD& tokens); + bool postBuild() override; - SearchQuery(); - }; - - struct _Params : public LLInitParam::Block<_Params, LLFloaterWebContent::Params> - { - Optional search; - }; - - typedef LLSDParamAdapter<_Params> Params; - - LLFloaterSearch(const Params& key); - - /// show the search floater with a new search - /// see search() for details on the key parameter. - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ void onClose(bool app_quitting); - - /// perform a search with the specific search term. - /// The key should be a map that can contain the following keys: - /// - "id": specifies the text phrase to search for - /// - "category": one of "all" (default), "people", "places", - /// "events", "groups", "wiki", "destinations", "classifieds" - void search(const SearchQuery &query); - - /// changing godmode can affect the search results that are - /// returned by the search website - use this method to tell the - /// search floater that the user has changed god level. - void godLevelChanged(U8 godlevel); - -private: - /*virtual*/ bool postBuild(); - - std::set mSearchType; - std::set mCollectionType; - U8 mSearchGodLevel; + std::set mSearchType; + std::set mCollectionType; }; - -#endif // LL_LLFLOATERSEARCH_H - diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index c668d414d3..a0621bb015 100644 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -68,9 +68,10 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) if (!folder->areChildrenInited() || !needsSort(folder->getViewModelItem())) return; - LLFolderViewModelItemInventory* modelp = static_cast(folder->getViewModelItem()); - if (modelp->getUUID().isNull()) return; + LLFolderViewModelItemInventory* sort_modelp = static_cast(folder->getViewModelItem()); + if (!sort_modelp->canSortContent()) return; + bool has_favorites = false; for (std::list::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd(); it != end_it; ++it) @@ -79,11 +80,14 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) LLFolderViewFolder* child_folderp = *it; sort(child_folderp); + LLFolderViewModelItemInventory* modelp = static_cast(child_folderp->getViewModelItem()); + has_favorites |= child_folderp->isFavorite() || child_folderp->hasFavorites(); + if (child_folderp->getFoldersCount() > 0) { - time_t most_recent_folder_time = - static_cast((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); - LLFolderViewModelItemInventory* modelp = static_cast(child_folderp->getViewModelItem()); + LLFolderViewModelItemInventory* folderp = static_cast((*child_folderp->getFoldersBegin())->getViewModelItem()); + time_t most_recent_folder_time = folderp->getCreationDate(); + if (most_recent_folder_time > modelp->getCreationDate()) { modelp->setCreationDate(most_recent_folder_time); @@ -91,16 +95,26 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) } if (child_folderp->getItemsCount() > 0) { - time_t most_recent_item_time = - static_cast((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); + LLFolderViewModelItemInventory* itemp = static_cast((*child_folderp->getItemsBegin())->getViewModelItem()); + time_t most_recent_item_time = itemp->getCreationDate(); - LLFolderViewModelItemInventory* modelp = static_cast(child_folderp->getViewModelItem()); if (most_recent_item_time > modelp->getCreationDate()) { modelp->setCreationDate(most_recent_item_time); } } } + for (std::list::const_iterator it = folder->getItemsBegin(), end_it = folder->getItemsEnd(); + it != end_it && !has_favorites; + ++it) + { + LLFolderViewItem* child_itemp = *it; + has_favorites |= child_itemp->isFavorite(); + } + if (has_favorites) + { + folder->updateHasFavorites(true); + } base_t::sort(folder); } diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index 48b4ee5fd9..74645a19e0 100644 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -48,6 +48,7 @@ public: virtual bool isItemInTrash( void) const { return false; } // TODO: make into pure virtual. virtual bool isItemInOutfits() const { return false; } virtual bool isAgentInventory() const { return false; } + virtual bool isAgentInventoryRoot() const { return false; } virtual bool isUpToDate() const = 0; virtual void addChild(LLFolderViewModelItem* child); virtual bool hasChildren() const = 0; @@ -58,6 +59,7 @@ public: virtual EInventorySortGroup getSortGroup() const = 0; virtual LLInventoryObject* getInventoryObject() const = 0; virtual void requestSort(); + virtual bool canSortContent() const { return getUUID().notNull(); } virtual void setPassedFilter(bool filtered, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0); virtual bool filter( LLFolderViewFilter& filter); virtual bool filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter); diff --git a/indra/newview/llgltffolderitem.h b/indra/newview/llgltffolderitem.h index 89d90c81cc..40a4c6fef1 100644 --- a/indra/newview/llgltffolderitem.h +++ b/indra/newview/llgltffolderitem.h @@ -114,6 +114,11 @@ public: EType getType() const { return mItemType; } S32 getItemId() const { return mItemId; } + bool isFavorite() const override { return false; } + bool isItemInTrash() const override { return false; } + bool isAgentInventory() const override { return false; } + bool isAgentInventoryRoot() const override { return false; } + private: LLUIImagePtr pIcon; std::string mName; diff --git a/indra/newview/llhudeffectresetskeleton.cpp b/indra/newview/llhudeffectresetskeleton.cpp new file mode 100644 index 0000000000..31065a3e76 --- /dev/null +++ b/indra/newview/llhudeffectresetskeleton.cpp @@ -0,0 +1,211 @@ +/** + * @file llhudeffectresetskeleton.cpp + * @brief LLHUDEffectResetSkeleton class implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llhudeffectresetskeleton.h" + +#include "llagent.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "message.h" + +// packet layout +const S32 TARGET_OBJECT = 0; // This is to allow for targetting owned animesh +const S32 RESET_ANIMATIONS = 16; //This can also be a flags if needed +const S32 PKT_SIZE = 17; + +//----------------------------------------------------------------------------- +// LLHUDEffectResetSkeleton() +//----------------------------------------------------------------------------- +LLHUDEffectResetSkeleton::LLHUDEffectResetSkeleton(const U8 type) : + LLHUDEffect(type) +{ +} + +//----------------------------------------------------------------------------- +// ~LLHUDEffectResetSkeleton() +//----------------------------------------------------------------------------- +LLHUDEffectResetSkeleton::~LLHUDEffectResetSkeleton() +{ +} + +//----------------------------------------------------------------------------- +// packData() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::packData(LLMessageSystem *mesgsys) +{ + // Pack the default data + LLHUDEffect::packData(mesgsys); + + // Pack the type-specific data. Uses a fun packed binary format. Whee! + U8 packed_data[PKT_SIZE]; + memset(packed_data, 0, PKT_SIZE); + + // pack both target object and position + // position interpreted as offset if target object is non-null + if (mTargetObject) + { + htolememcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16); + } + else + { + htolememcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16); + } + + U8 resetAnimations = (U8)mResetAnimations; + htolememcpy(&(packed_data[RESET_ANIMATIONS]), &resetAnimations, MVT_U8, 1); + + mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE); +} + +//----------------------------------------------------------------------------- +// unpackData() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::unpackData(LLMessageSystem *mesgsys, S32 blocknum) +{ + LLVector3d new_target; + U8 packed_data[PKT_SIZE]; + + + LLHUDEffect::unpackData(mesgsys, blocknum); + + LLUUID source_id; + mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_AgentID, source_id, blocknum); + + LLViewerObject *objp = gObjectList.findObject(source_id); + if (objp && objp->isAvatar()) + { + setSourceObject(objp); + } + else + { + //LL_WARNS() << "Could not find source avatar for ResetSkeleton effect" << LL_ENDL; + return; + } + + S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData); + if (size != PKT_SIZE) + { + LL_WARNS() << "ResetSkeleton effect with bad size " << size << LL_ENDL; + return; + } + + mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum); + + LLUUID target_id; + htolememcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16); + + // The purpose for having a target ID is if we want to reset animesh, or + // other things in the future. + // I implemented this, but due to issues regarding various permission + // checks, I scrapped it for now. --Chaser Zaks + // See https://github.com/secondlife/viewer/pull/1212 for additional info + + if (target_id.isNull()) + { + target_id = source_id; + } + + objp = gObjectList.findObject(target_id); + + if (objp) + { + setTargetObject(objp); + } + + U8 resetAnimations = 0; + htolememcpy(&resetAnimations, &(packed_data[RESET_ANIMATIONS]), MVT_U8, 1); + + // Pre-emptively assume this is going to be flags in the future. + // It isn't needed now, but this will assure that only bit 1 is set + mResetAnimations = resetAnimations & 1; + + update(); +} + +//----------------------------------------------------------------------------- +// setTargetObjectAndOffset() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::setTargetObject(LLViewerObject *objp) +{ + mTargetObject = objp; +} + + +//----------------------------------------------------------------------------- +// markDead() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::markDead() +{ + LLHUDEffect::markDead(); +} + +void LLHUDEffectResetSkeleton::setSourceObject(LLViewerObject* objectp) +{ + // restrict source objects to avatars + if (objectp && objectp->isAvatar()) + { + LLHUDEffect::setSourceObject(objectp); + } +} + +//----------------------------------------------------------------------------- +// update() +//----------------------------------------------------------------------------- +void LLHUDEffectResetSkeleton::update() +{ + // If the target object is dead, set the target object to NULL + if (mTargetObject.isNull() || mTargetObject->isDead()) + { + markDead(); + return; + } + + if (mSourceObject.isNull() || mSourceObject->isDead()) + { + markDead(); + return; + } + + if (mTargetObject->isAvatar()) + { + // Only the owner of a avatar can reset their skeleton like this + // Also allow reset if we created the effect (Local resetting) + if (mSourceObject->getID() == mTargetObject->getID() || getOriginatedHere()) + { + LLVOAvatar* avatar = mTargetObject->asAvatar(); + avatar->resetSkeleton(mResetAnimations); + } + } + else + { + LL_WARNS() << mSourceObject->getID() << " attempted to reset skeleton on " + << mTargetObject->getID() << ", but it is not a avatar!" << LL_ENDL; + } + + markDead(); +} diff --git a/indra/newview/llhudeffectresetskeleton.h b/indra/newview/llhudeffectresetskeleton.h new file mode 100644 index 0000000000..39a6137054 --- /dev/null +++ b/indra/newview/llhudeffectresetskeleton.h @@ -0,0 +1,59 @@ +/** + * @file llhudeffectresetskeleton.h + * @brief LLHUDEffectResetSkeleton class definition + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLHUDEFFECTRESETSKELETON_H +#define LL_LLHUDEFFECTRESETSKELETON_H + +#include "llhudeffect.h" + +class LLViewerObject; +class LLVOAvatar; + + +class LLHUDEffectResetSkeleton final : public LLHUDEffect +{ +public: + friend class LLHUDObject; + + /*virtual*/ void markDead(); + /*virtual*/ void setSourceObject(LLViewerObject* objectp); + + void setTargetObject(LLViewerObject *objp); + void setResetAnimations(bool enable){ mResetAnimations = enable; }; + +protected: + LLHUDEffectResetSkeleton(const U8 type); + ~LLHUDEffectResetSkeleton(); + + /*virtual*/ void packData(LLMessageSystem *mesgsys); + /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum); + + void update(); +private: + bool mResetAnimations; +}; + +#endif // LL_LLHUDEFFECTRESETSKELETON_H diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index e6fbfbfb38..04e9e2dff2 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -36,6 +36,7 @@ #include "llhudeffecttrail.h" #include "llhudeffectlookat.h" #include "llhudeffectpointat.h" +#include "llhudeffectresetskeleton.h" #include "llhudnametag.h" #include "llvoicevisualizer.h" @@ -241,6 +242,9 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) case LL_HUD_EFFECT_BLOB: hud_objectp = new LLHUDEffectBlob(type); break; + case LL_HUD_EFFECT_RESET_SKELETON: + hud_objectp = new LLHUDEffectResetSkeleton(type); + break; default: LL_WARNS() << "Unknown type of hud effect:" << (U32) type << LL_ENDL; } diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 8c628e3f92..f683f21e96 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -96,7 +96,8 @@ public: LL_HUD_EFFECT_POINTAT, LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella LL_HUD_NAME_TAG, - LL_HUD_EFFECT_BLOB + LL_HUD_EFFECT_BLOB, + LL_HUD_EFFECT_RESET_SKELETON }; protected: static void sortObjects(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index f0f25089fa..c1e80ba4f1 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -1700,6 +1700,8 @@ bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, } else { + // will check KeepConversationLogTranscripts on its own + LLConversationLog::instance().cache(); return false; } } diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp index 24dbe61bad..9f0d236826 100644 --- a/indra/newview/llinspecttexture.cpp +++ b/indra/newview/llinspecttexture.cpp @@ -115,7 +115,6 @@ public: protected: LLPointer m_Image; - S32 mImageBoostLevel = LLGLTexture::BOOST_NONE; std::string mLoadingText; }; @@ -128,12 +127,8 @@ LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p) LLTexturePreviewView::~LLTexturePreviewView() { - if (m_Image) - { - m_Image->setBoostLevel(mImageBoostLevel); m_Image = nullptr; } -} void LLTexturePreviewView::draw() { @@ -153,18 +148,18 @@ void LLTexturePreviewView::draw() bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0); if (isLoading) LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, rctClient.mLeft + 3, rctClient.mTop - 25, LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); - m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight())); + + m_Image->setKnownDrawSize(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE); } } void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset) { - m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_THUMBNAIL); if (m_Image) { - mImageBoostLevel = m_Image->getBoostLevel(); - m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW); m_Image->forceToSaveRawImage(0); + m_Image->setKnownDrawSize(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE); if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) ) { if (m_Image->isInFastCacheList()) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index f9e9049119..331754d009 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -829,6 +829,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, { const LLInventoryObject *obj = getInventoryObject(); bool single_folder_root = (mRoot == NULL); + bool is_cof = isCOFFolder(); + bool is_inbox = isInboxFolder(); if (obj) { @@ -843,7 +845,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy")); } - if (isAgentInventory() && !single_folder_root && !isMarketplaceListingsFolder()) + bool is_agent_inventory = isAgentInventory(); + if (is_agent_inventory && !single_folder_root && !is_cof && !is_inbox) { items.push_back(std::string("New folder from selected")); items.push_back(std::string("Subfolder Separator")); @@ -856,6 +859,19 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } } + if (isFavorite()) + { + items.push_back(std::string("Remove from Favorites")); + } + else if (is_agent_inventory && !gInventory.isObjectDescendentOf(mUUID, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) + { + items.push_back(std::string("Add to Favorites")); + if (gInventory.getRootFolderID() == mUUID) + { + disabled_items.push_back(std::string("Add to Favorites")); + } + } + if (obj->getIsLinkType()) { items.push_back(std::string("Find Original")); @@ -868,6 +884,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if (!isItemMovable() || !canMenuCut()) { disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("New folder from selected")); } } else @@ -877,7 +894,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, items.push_back(std::string("Find Links")); } - if (!isInboxFolder() && !single_folder_root) + if (!is_inbox && !single_folder_root) { items.push_back(std::string("Rename")); if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) @@ -917,6 +934,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, if (!isItemMovable() || !canMenuCut()) { disabled_items.push_back(std::string("Cut")); + disabled_items.push_back(std::string("New folder from selected")); } if (canListOnMarketplace() && !isMarketplaceListingsFolder() && !isInboxFolder()) @@ -939,7 +957,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } // Don't allow items to be pasted directly into the COF or the inbox - if (!isCOFFolder() && !isInboxFolder()) + if (!is_cof && !is_inbox) { items.push_back(std::string("Paste")); } @@ -1333,6 +1351,13 @@ bool LLInvFVBridge::isAgentInventory() const return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); } +bool LLInvFVBridge::isAgentInventoryRoot() const +{ + const LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + return gInventory.getRootFolderID() == mUUID; +} + bool LLInvFVBridge::isCOFFolder() const { return LLAppearanceMgr::instance().getIsInCOF(mUUID); @@ -2280,7 +2305,21 @@ const LLUUID& LLItemBridge::getThumbnailUUID() const return LLUUID::null; } -// virtual +bool LLItemBridge::isFavorite() const +{ + LLViewerInventoryItem* item = NULL; + LLInventoryModel* model = getInventoryModel(); + if (model) + { + item = model->getItem(mUUID); + } + if (item) + { + return get_is_favorite(item); + } + return false; +} + bool LLItemBridge::isItemPermissive() const { if (LLViewerInventoryItem* item = getItem()) @@ -2425,6 +2464,16 @@ const LLUUID& LLFolderBridge::getThumbnailUUID() const return LLUUID::null; } +bool LLFolderBridge::isFavorite() const +{ + LLViewerInventoryCategory* cat = getCategory(); + if (cat) + { + return cat->getIsFavorite(); + } + return false; +} + void LLFolderBridge::update() { // we know we have children but haven't fetched them (doesn't obey filter) @@ -4420,6 +4469,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items } disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("upload_options")); disabled_items.push_back(std::string("upload_def")); disabled_items.push_back(std::string("create_new")); } @@ -4445,6 +4495,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { disabled_items.push_back(std::string("New Folder")); disabled_items.push_back(std::string("New Listing Folder")); + disabled_items.push_back(std::string("upload_options")); disabled_items.push_back(std::string("upload_def")); disabled_items.push_back(std::string("create_new")); } @@ -4504,6 +4555,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items items.push_back(std::string("Rename")); items.push_back(std::string("thumbnail")); + addInventoryFavoritesMenuOptions(items); addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); @@ -4521,6 +4573,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items EMyOutfitsSubfolderType in_my_outfits = myoutfit_object_subfolder_type(model, mUUID, outfits_id); if (in_my_outfits != MY_OUTFITS_NO) { + // Either an outfit or a subfolder inside MY_OUTFITS if (in_my_outfits == MY_OUTFITS_SUBFOLDER) { // Not inside an outfit, but inside 'my outfits' @@ -4530,6 +4583,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items items.push_back(std::string("Rename")); items.push_back(std::string("thumbnail")); + addInventoryFavoritesMenuOptions(items); addDeleteContextMenuOptions(items, disabled_items); } else @@ -4546,6 +4600,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items } if (!isMarketplaceListingsFolder()) { + items.push_back(std::string("upload_options")); items.push_back(std::string("upload_def")); items.push_back(std::string("create_new")); items.push_back(std::string("New Script")); @@ -4577,6 +4632,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID) { items.push_back(std::string("Copy outfit list to clipboard")); + addInventoryFavoritesMenuOptions(items); + addOpenFolderMenuOptions(flags, items); } @@ -4835,6 +4892,18 @@ void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items) } } +void LLFolderBridge::addInventoryFavoritesMenuOptions(menuentry_vec_t& items) +{ + if (isFavorite()) + { + items.push_back(std::string("Remove from Favorites")); + } + else + { + items.push_back(std::string("Add to Favorites")); + } +} + bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); @@ -7089,12 +7158,13 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) item = (LLViewerInventoryItem*)gInventory.getItem(object_id); if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID())) { - rez_attachment(item, NULL, true); // Replace if "Wear"ing. + static LLCachedControl replace_item(gSavedSettings, "InventoryAddAttachmentBehavior", false); + rez_attachment(item, NULL, ("attach" == action) ? replace_item() : true); // Replace if "Wear"ing. } else if(item && item->isFinished()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, true)); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), @@ -8219,7 +8289,8 @@ void LLObjectBridgeAction::attachOrDetach() } else { - LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. + static LLCachedControl inventory_linking(gSavedSettings, "InventoryAddAttachmentBehavior", false); + LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, inventory_linking()); // Don't replace if adding. } } @@ -8410,6 +8481,7 @@ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) buildContextMenuOptions(flags, items, disabled_items); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); + items.erase(std::remove(items.begin(), items.end(), std::string("New folder from selected")), items.end()); hide_context_entries(menu, items, disabled_items); } @@ -8444,6 +8516,51 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( return new_listener; } +/************************************************************************/ +/* Favorites Inventory Panel related classes */ +/************************************************************************/ +void LLFavoritesFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // todo: consider things that should be disabled + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); + + items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); + items.erase(std::remove(items.begin(), items.end(), std::string("New folder from selected")), items.end()); + + hide_context_entries(menu, items, disabled_items); +} + +LLInvFVBridge* LLFavoritesInventoryBridgeBuilder::createBridge( + LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, + LLFolderView* root, + const LLUUID& uuid, + U32 flags /*= 0x00*/) const +{ + LLInvFVBridge* new_listener = NULL; + if (asset_type == LLAssetType::AT_CATEGORY + && actual_asset_type != LLAssetType::AT_LINK_FOLDER) + { + new_listener = new LLFavoritesFolderBridge(inv_type, inventory, root, uuid); + } + else + { + new_listener = LLInventoryFolderViewModelBuilder::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + view_model, + root, + uuid, + flags); + } + return new_listener; +} + LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge() { } @@ -8454,7 +8571,7 @@ void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_dequ menuentry_vec_t disabled_items; if (get_selection_item_uuids(selected_items, ids)) { - if (!LLAppearanceMgr::instance().canAddWearables(ids) && canWearSelected(ids)) + if (!LLAppearanceMgr::instance().canAddWearables(ids, false) && canWearSelected(ids)) { disabled_items.push_back(std::string("Wearable And Object Wear")); disabled_items.push_back(std::string("Wearable Add")); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index b7bdef9b21..d96adbd1d2 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -86,6 +86,7 @@ public: //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null; } + virtual bool isFavorite() const { return false; } virtual void clearDisplayName() { mDisplayName.clear(); } virtual void restoreItem() {} virtual void restoreToWorld() {} @@ -175,6 +176,7 @@ protected: bool isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? bool isAgentInventory() const; // false if lost or in the inventory library + bool isAgentInventoryRoot() const; // true if worn by agent bool isCOFFolder() const; // true if COF or descendant of bool isInboxFolder() const; // true if COF or descendant of marketplace inbox @@ -259,6 +261,7 @@ public: LLViewerInventoryItem* getItem() const; virtual const LLUUID& getThumbnailUUID() const; + virtual bool isFavorite() const; protected: bool confirmRemoveItem(const LLSD& notification, const LLSD& response); @@ -301,6 +304,7 @@ public: virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; virtual const LLUUID& getThumbnailUUID() const; + virtual bool isFavorite() const; void setShowDescendantsCount(bool show_count) {mShowDescendantsCount = show_count;} @@ -341,6 +345,7 @@ protected: void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items); + void addInventoryFavoritesMenuOptions(menuentry_vec_t& items); // Inventory favorites, not toolbar favorites //-------------------------------------------------------------------- // Menu callbacks @@ -754,6 +759,46 @@ public: U32 flags = 0x00) const; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Favorites Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Overridden version of the Inventory-Folder-View-Bridge for Folders +class LLFavoritesFolderBridge : public LLFolderBridge +{ + friend class LLInvFVBridgeAction; +public: + // Creates context menu for Folders related to Recent Inventory Panel. + // Uses base logic and than removes from visible items "New..." menu items. + LLFavoritesFolderBridge(LLInventoryType::EType type, + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLFolderBridge(inventory, root, uuid) + { + mInvType = type; + } + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); + /*virtual*/ bool canSortContent() const { return true; } +}; + +// Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel +class LLFavoritesInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder +{ +public: + LLFavoritesInventoryBridgeBuilder() {} + // Overrides FolderBridge for Recent Inventory Panel. + // It use base functionality for bridges other than FolderBridge. + virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, + LLAssetType::EType actual_asset_type, + LLInventoryType::EType inv_type, + LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, + LLFolderView* root, + const LLUUID& uuid, + U32 flags = 0x00) const; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Marketplace Inventory Panel related classes //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -781,7 +826,7 @@ private: void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, - bool replace = false); + bool replace); // Move items from an in-world object's "Contents" folder to a specified // folder in agent inventory. diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 01f2c6c525..99c2d6e410 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -64,6 +64,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p) mFilterUUID(p.uuid), mFilterLinks(p.links), mFilterThumbnails(p.thumbnails), + mFilterFavorites(p.favorites), mSearchVisibility(p.search_visibility) { } @@ -159,6 +160,7 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) passed = passed && checkAgainstCreator(listener); passed = passed && checkAgainstSearchVisibility(listener); + passed = passed && checkAgainstFilterFavorites(listener->getUUID()); passed = passed && checkAgainstFilterThumbnails(listener->getUUID()); return passed; @@ -221,6 +223,19 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return false; } + const LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (cat && cat->getIsFavorite()) + { + if (mFilterOps.mFilterFavorites == FILTER_ONLY_FAVORITES) + { + return true; + } + if (mFilterOps.mFilterFavorites == FILTER_EXCLUDE_FAVORITES) + { + return false; + } + } + // Marketplace folder filtering const U32 filterTypes = mFilterOps.mFilterTypes; const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | @@ -273,6 +288,16 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const } } + if (filterTypes & FILTERTYPE_NO_TRASH_ITEMS) + { + const LLUUID trash_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + // If not a descendant of the marketplace listings root, then the nesting depth is -1 by definition + if (gInventory.isObjectDescendentOf(folder_id, trash_uuid)) + { + return false; + } + } + // show folder links LLViewerInventoryItem* item = gInventory.getItem(folder_id); if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER) @@ -611,6 +636,24 @@ bool LLInventoryFilter::checkAgainstFilterThumbnails(const LLUUID& object_id) co return true; } +bool LLInventoryFilter::checkAgainstFilterFavorites(const LLUUID& object_id) const +{ + const LLInventoryObject* object = gInventory.getObject(object_id); + if (!object) return true; + + + if (mFilterOps.mFilterFavorites != FILTER_INCLUDE_FAVORITES) + { + bool is_favorite = get_is_favorite(object); + if (is_favorite && (mFilterOps.mFilterFavorites == FILTER_EXCLUDE_FAVORITES)) + return false; + if (!is_favorite && (mFilterOps.mFilterFavorites == FILTER_ONLY_FAVORITES)) + return false; + } + + return true; +} + bool LLInventoryFilter::checkAgainstCreator(const LLFolderViewModelItemInventory* listener) const { if (!listener) @@ -811,6 +854,32 @@ void LLInventoryFilter::setFilterThumbnails(U64 filter_thumbnails) mFilterOps.mFilterThumbnails = filter_thumbnails; } +void LLInventoryFilter::setFilterFavorites(U64 filter_favorites) +{ + if (mFilterOps.mFilterFavorites != filter_favorites) + { + if (mFilterOps.mFilterFavorites == FILTER_EXCLUDE_FAVORITES + && filter_favorites == FILTER_ONLY_FAVORITES) + { + setModified(FILTER_RESTART); + } + else if (mFilterOps.mFilterFavorites == FILTER_ONLY_FAVORITES + && filter_favorites == FILTER_EXCLUDE_FAVORITES) + { + setModified(FILTER_RESTART); + } + else if (mFilterOps.mFilterFavorites == FILTER_INCLUDE_FAVORITES) + { + setModified(FILTER_MORE_RESTRICTIVE); + } + else + { + setModified(FILTER_LESS_RESTRICTIVE); + } + } + mFilterOps.mFilterFavorites = filter_favorites; +} + void LLInventoryFilter::setFilterEmptySystemFolders() { mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; @@ -923,6 +992,11 @@ void LLInventoryFilter::toggleSearchVisibilityLibrary() } } +void LLInventoryFilter::setFilterNoTrashFolder() +{ + mFilterOps.mFilterTypes |= FILTERTYPE_NO_TRASH_ITEMS; +} + void LLInventoryFilter::setFilterNoMarketplaceFolder() { mFilterOps.mFilterTypes |= FILTERTYPE_NO_MARKETPLACE_ITEMS; @@ -1615,6 +1689,11 @@ U64 LLInventoryFilter::getFilterThumbnails() const return mFilterOps.mFilterThumbnails; } +U64 LLInventoryFilter::getFilterFavorites() const +{ + return mFilterOps.mFilterFavorites; +} + bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 612a161ba2..c0164e04e4 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -61,6 +61,7 @@ public: FILTERTYPE_NO_MARKETPLACE_ITEMS = 0x1 << 10, // pass iff folder is not under the marketplace FILTERTYPE_WORN = 0x1 << 11, // pass if item is worn FILTERTYPE_SETTINGS = 0x1 << 12, // pass if the item is a settings object + FILTERTYPE_NO_TRASH_ITEMS = 0x1 << 13, // pass iff folder is not under the marketplace }; enum EFilterDateDirection @@ -83,6 +84,13 @@ public: FILTER_ONLY_THUMBNAILS }; + enum EFilterFavorite + { + FILTER_INCLUDE_FAVORITES, + FILTER_EXCLUDE_FAVORITES, + FILTER_ONLY_FAVORITES + }; + enum ESortOrderType { SO_NAME = 0, // Sort inventory by name @@ -149,6 +157,7 @@ public: Optional permissions; Optional creator_type; Optional thumbnails; + Optional favorites; Params() : types("filter_types", FILTERTYPE_OBJECT), @@ -156,6 +165,7 @@ public: wearable_types("wearable_types", 0xffffFFFFffffFFFFULL), settings_types("settings_types", 0xffffFFFFffffFFFFULL), thumbnails("thumbnails", FILTER_INCLUDE_THUMBNAILS), + favorites("favorites", FILTER_INCLUDE_FAVORITES), category_types("category_types", 0xffffFFFFffffFFFFULL), links("links", FILTERLINK_INCLUDE_LINKS), search_visibility("search_visibility", 0xFFFFFFFF), @@ -177,6 +187,7 @@ public: mFilterWearableTypes, mFilterSettingsTypes, // for _SETTINGS mFilterThumbnails, + mFilterFavorites, mFilterLinks, mFilterCategoryTypes; // For _CATEGORY LLUUID mFilterUUID; // for UUID @@ -220,6 +231,7 @@ public: U64 getFilterSettingsTypes() const; U64 getSearchVisibilityTypes() const; U64 getFilterThumbnails() const; + U64 getFilterFavorites() const; bool isFilterObjectTypesWith(LLInventoryType::EType t) const; void setFilterObjectTypes(U64 types); @@ -233,8 +245,10 @@ public: void setFilterMarketplaceInactiveFolders(); void setFilterMarketplaceUnassociatedFolders(); void setFilterMarketplaceListingFolders(bool select_only_listing_folders); + void setFilterNoTrashFolder(); void setFilterNoMarketplaceFolder(); void setFilterThumbnails(U64 filter_thumbnails); + void setFilterFavorites(U64 filter_favorites); void updateFilterTypes(U64 types, U64& current_types); void setSearchType(ESearchType type); ESearchType getSearchType() { return mSearchType; } @@ -339,6 +353,7 @@ public: LLInventoryFilter& operator =(const LLInventoryFilter& other); bool checkAgainstFilterThumbnails(const LLUUID& object_id) const; + bool checkAgainstFilterFavorites(const LLUUID& object_id) const; private: bool areDateLimitsSet() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index d2ed7168c5..d1fd82a7f1 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -51,6 +51,7 @@ #include "lldirpicker.h" #include "lldonotdisturbnotificationstorage.h" #include "llfloatermarketplacelistings.h" +#include "llfloatermodelpreview.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "llfolderview.h" @@ -62,6 +63,7 @@ #include "llinventorymodel.h" #include "llinventorypanel.h" #include "lllineeditor.h" +#include "llmaterialeditor.h" #include "llmarketplacenotifications.h" #include "llmarketplacefunctions.h" #include "llmenugl.h" @@ -86,6 +88,7 @@ #include "llviewermessage.h" #include "llviewerfoldertype.h" #include "llviewerobjectlist.h" +#include "llviewermenufile.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvoavatarself.h" @@ -2172,21 +2175,10 @@ void validate_marketplacelistings( void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) { - LLInventoryItem* inv_item = gInventory.getItem(item_id); + LLViewerInventoryItem* inv_item = gInventory.getItem(item_id); if (inv_item) { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(inv_item->getParentUUID(), -1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer new_item = new LLViewerInventoryItem(inv_item); - new_item->setParent(new_parent_id); - new_item->updateParentOnServer(false); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); + gInventory.changeItemParent(inv_item, new_parent_id, false); } } @@ -2194,17 +2186,17 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected { for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) { - LLInventoryItem* inv_item = gInventory.getItem(*it); + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); if (inv_item) { - change_item_parent(*it, new_cat_uuid); + gInventory.changeItemParent(inv_item, new_cat_uuid, false); } else { - LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); if (inv_cat && !LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) { - gInventory.changeCategoryParent((LLViewerInventoryCategory*)inv_cat, new_cat_uuid, false); + gInventory.changeCategoryParent(inv_cat, new_cat_uuid, false); } } } @@ -2488,6 +2480,143 @@ void ungroup_folder_items(const LLUUID& folder_id) gInventory.notifyObservers(); } +class LLUpdateFavorite : public LLInventoryCallback +{ +public: + LLUpdateFavorite(const LLUUID& inv_item_id) + : mInvItemID(inv_item_id) + {} + /* virtual */ void fire(const LLUUID& inv_item_id) override + { + gInventory.addChangedMask(LLInventoryObserver::UPDATE_FAVORITE, mInvItemID); + + LLInventoryModel::item_array_t items; + LLInventoryModel::cat_array_t cat_array; + LLLinkedItemIDMatches matches(mInvItemID); + gInventory.collectDescendentsIf(gInventory.getRootFolderID(), + cat_array, + items, + LLInventoryModel::INCLUDE_TRASH, + matches); + + std::set link_ids; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) + { + LLPointer item = *it; + + gInventory.addChangedMask(LLInventoryObserver::UPDATE_FAVORITE, item->getUUID()); + } + + gInventory.notifyObservers(); + } +private: + LLUUID mInvItemID; +}; + +void favorite_send(LLInventoryObject* obj, const LLUUID& obj_id, bool favorite) +{ + LLSD updates; + if (favorite) + { + updates["favorite"] = LLSD().with("toggled", true); + } + else + { + updates["favorite"] = LLSD(); + } + + LLPointer cb = new LLUpdateFavorite(obj_id); + + LLViewerInventoryCategory* view_folder = dynamic_cast(obj); + if (view_folder) + { + update_inventory_category(obj_id, updates, cb); + } + LLViewerInventoryItem* view_item = dynamic_cast(obj); + if (view_item) + { + update_inventory_item(obj_id, updates, cb); + } +} + +bool get_is_favorite(const LLInventoryObject* object) +{ + if (object->getIsLinkType()) + { + LLInventoryObject* obj = gInventory.getObject(object->getLinkedUUID()); + return obj && obj->getIsFavorite(); + } + + return object->getIsFavorite(); +} + +bool get_is_favorite(const LLUUID& obj_id) +{ + LLInventoryObject* object = gInventory.getObject(obj_id); + if (object && object->getIsLinkType()) + { + LLInventoryObject* obj = gInventory.getObject(object->getLinkedUUID()); + return obj && obj->getIsFavorite(); + } + + return object->getIsFavorite(); +} + +void set_favorite(const LLUUID& obj_id, bool favorite) +{ + LLInventoryObject* obj = gInventory.getObject(obj_id); + + if (obj && obj->getIsLinkType()) + { + if (!favorite && obj->getIsFavorite()) + { + // Links currently aren't supposed to be favorites, + // instead should show state of the original + LL_INFOS("Inventory") << "Recovering proper 'favorites' state of a link " << obj_id << LL_ENDL; + favorite_send(obj, obj_id, false); + } + obj = gInventory.getObject(obj->getLinkedUUID()); + } + + if (obj && obj->getIsFavorite() != favorite) + { + favorite_send(obj, obj->getUUID(), favorite); + } +} + +void toggle_favorite(const LLUUID& obj_id) +{ + LLInventoryObject* obj = gInventory.getObject(obj_id); + if (obj && obj->getIsLinkType()) + { + obj = gInventory.getObject(obj->getLinkedUUID()); + } + + if (obj) + { + favorite_send(obj, obj->getUUID(), !obj->getIsFavorite()); + } +} + +void toggle_favorites(const uuid_vec_t& ids) +{ + if (ids.size() == 0) + { + return; + } + if (ids.size() == 1) + { + toggle_favorite(ids[0]); + return; + } + + bool new_val = !get_is_favorite(ids.front()); + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + set_favorite(*it, new_val); + } +} + std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) { if (model) @@ -2822,6 +2951,20 @@ bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryIt return false; } +bool LLFavoritesCollector::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if (item && item->getIsFavorite()) + { + return true; + } + if (cat && cat->getIsFavorite()) + { + return true; + } + return false; +} + bool LLBuddyCollector::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -3306,7 +3449,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root for (LLInventoryModel::item_array_t::value_type& item : items) { - if (get_is_item_worn(item)) + if (!item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -3326,7 +3469,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } } LLViewerInventoryItem* item = gInventory.getItem(obj_id); - if (item && get_is_item_worn(item)) + if (item && !item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -3532,7 +3675,6 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } else if ("new_folder_from_selected" == action) { - LLInventoryObject* first_item = gInventory.getObject(*ids.begin()); if (!first_item) { @@ -3576,6 +3718,20 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root ungroup_folder_items(*ids.begin()); } } + else if ("add_to_favorites" == action) + { + for (const LLUUID& id : ids) + { + set_favorite(id, true); + } + } + else if ("remove_from_favorites" == action) + { + for (const LLUUID& id : ids) + { + set_favorite(id, false); + } + } else if ("thumbnail" == action) { if (selected_items.size() > 0) @@ -3686,6 +3842,54 @@ void LLInventoryAction::removeItemFromDND(LLFolderView* root) } } +void LLInventoryAction::fileUploadLocation(const LLUUID& dest_id, const std::string& action) +{ + if (action == "def_model") + { + gSavedPerAccountSettings.setString("ModelUploadFolder", dest_id.asString()); + } + else if (action == "def_texture") + { + gSavedPerAccountSettings.setString("TextureUploadFolder", dest_id.asString()); + } + else if (action == "def_sound") + { + gSavedPerAccountSettings.setString("SoundUploadFolder", dest_id.asString()); + } + else if (action == "def_animation") + { + gSavedPerAccountSettings.setString("AnimationUploadFolder", dest_id.asString()); + } + else if (action == "def_pbr_material") + { + gSavedPerAccountSettings.setString("PBRUploadFolder", dest_id.asString()); + } + else if (action == "upload_texture") + { + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, dest_id), LLFilePicker::FFLOAD_IMAGE, false); + } + else if (action == "upload_sound") + { + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, dest_id), LLFilePicker::FFLOAD_WAV, false); + } + else if (action == "upload_animation") + { + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, dest_id), LLFilePicker::FFLOAD_ANIM, false); + } + else if (action == "upload_model") + { + LLFloaterModelPreview::showModelPreview(dest_id); + } + else if (action == "upload_pbr_material") + { + LLMaterialEditor::importMaterial(dest_id); + } + else if (action == "upload_bulk") + { + LLFilePickerReplyThread::startPicker(boost::bind(&upload_bulk, _1, _2, true, dest_id), LLFilePicker::FFLOAD_ALL, true); + } +} + void LLInventoryAction::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle root) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -3794,15 +3998,17 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) for (; set_iter != selected_items.end(); ++set_iter) { viewModel = dynamic_cast((*set_iter)->getViewModelItem()); - if (!viewModel || !viewModel->getInventoryObject()) continue; - if (gInventory.isObjectDescendentOf(viewModel->getInventoryObject()->getParentUUID(), marketplacelistings_id)) + if (!viewModel) continue; + LLInventoryObject* inv_obj = viewModel->getInventoryObject(); + if (!inv_obj) continue; + if (gInventory.isObjectDescendentOf(inv_obj->getParentUUID(), marketplacelistings_id)) { - const LLUUID &parent_id = viewModel->getInventoryObject()->getParentUUID(); + const LLUUID &parent_id = inv_obj->getParentUUID(); if (parent_id != marketplacelistings_id) { sMarketplaceFolders.push_back(parent_id); } - const LLUUID &curr_id = viewModel->getInventoryObject()->getUUID(); + const LLUUID &curr_id = inv_obj->getUUID(); if (curr_id != marketplacelistings_id) { sMarketplaceFolders.push_back(curr_id); diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index b23f82a189..ae7bb8770d 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -118,6 +118,11 @@ bool can_move_to_my_outfits_as_subfolder(LLInventoryModel* model, LLInventoryCat std::string get_localized_folder_name(LLUUID cat_uuid); void new_folder_window(const LLUUID& folder_id); void ungroup_folder_items(const LLUUID& folder_id); +bool get_is_favorite(const LLInventoryObject* object); +bool get_is_favorite(const LLUUID& obj_id); +void set_favorite(const LLUUID& obj_id, bool favorite); +void toggle_favorite(const LLUUID& obj_id); +void toggle_favorites(const uuid_vec_t& ids); std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id); std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id); std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id); @@ -201,7 +206,9 @@ class LLInventoryCollectFunctor { public: virtual ~LLInventoryCollectFunctor(){}; - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; + + virtual bool exceedsLimit() { return false; } static bool itemTransferCommonlyAllowed(const LLInventoryItem* item); }; @@ -375,6 +382,18 @@ protected: LLUUID mGroupID; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFavoritesCollector +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFavoritesCollector : public LLInventoryCollectFunctor +{ +public: + LLFavoritesCollector() {} + virtual ~LLFavoritesCollector() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLBuddyCollector // @@ -633,6 +652,7 @@ struct LLInventoryAction static void callback_copySelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action); static void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle root); static void removeItemFromDND(LLFolderView* root); + static void fileUploadLocation(const LLUUID& dest_id, const std::string& action); static void saveMultipleTextures(const std::vector& filenames, std::set selected_items, LLInventoryModel* model); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 43d4edb069..a4cb6ea65d 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -636,7 +636,7 @@ void LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item) mItemPanels.pop_back(); } -LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn) +LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn, bool is_favorite) { LLInventoryGalleryItem::Params giparams; giparams.visible = true; @@ -647,6 +647,7 @@ LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, L gitem->setUUID(item_id); gitem->setGallery(this); gitem->setType(type, inventory_type, flags, is_link); + gitem->setFavorite(is_favorite); gitem->setLoadImmediately(mLoadThumbnailsImmediately); gitem->setThumbnail(thumbnail_id); gitem->setWorn(is_worn); @@ -939,8 +940,19 @@ bool LLInventoryGallery::updateAddedItem(LLUUID item_id) } bool res = false; + bool is_favorite = get_is_favorite(obj); - LLInventoryGalleryItem* item = buildGalleryItem(name, item_id, obj->getType(), thumbnail_id, inventory_type, misc_flags, obj->getCreationDate(), obj->getIsLinkType(), is_worn); + LLInventoryGalleryItem* item = buildGalleryItem( + name, + item_id, + obj->getType(), + thumbnail_id, + inventory_type, + misc_flags, + obj->getCreationDate(), + obj->getIsLinkType(), + is_worn, + is_favorite); mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item)); if (mGalleryCreated) { @@ -977,7 +989,7 @@ void LLInventoryGallery::updateRemovedItem(LLUUID item_id) mItemBuildQuery.erase(item_id); } -void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name) +void LLInventoryGallery::updateChangedItemData(LLUUID item_id, std::string name, bool is_favorite) { gallery_item_map_t::iterator iter = mItemMap.find(item_id); if (iter != mItemMap.end()) @@ -986,6 +998,7 @@ void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name) if (item) { item->setItemName(name); + item->setFavorite(is_favorite); } } } @@ -2001,7 +2014,7 @@ void LLInventoryGallery::deleteSelection() for (LLInventoryModel::item_array_t::value_type& item : items) { - if (get_is_item_worn(item)) + if (!item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -2022,7 +2035,7 @@ void LLInventoryGallery::deleteSelection() } LLViewerInventoryItem* item = gInventory.getItem(id); - if (item && get_is_item_worn(item)) + if (item && !item->getIsLinkType() && get_is_item_worn(item)) { has_worn = true; LLWearableType::EType type = item->getWearableType(); @@ -2335,7 +2348,7 @@ void LLInventoryGallery::refreshList(const LLUUID& category_id) return; } - updateChangedItemName(*items_iter, obj->getName()); + updateChangedItemData(*items_iter, obj->getName(), get_is_favorite(obj)); mNeedsArrange = true; } @@ -2851,6 +2864,14 @@ void LLInventoryGalleryItem::setType(LLAssetType::EType type, LLInventoryType::E getChild("link_overlay")->setVisible(is_link); } +void LLInventoryGalleryItem::setFavorite(bool is_favorite) +{ + getChild("fav_icon")->setVisible(is_favorite); + static const LLUIColor text_color = LLUIColorTable::instance().getColor("LabelTextColor", LLColor4::white); + static const LLUIColor favorite_color = LLUIColorTable::instance().getColor("InventoryFavoriteColor", LLColor4::white); + mNameText->setReadOnlyColor(is_favorite ? favorite_color : text_color); +} + void LLInventoryGalleryItem::setThumbnail(LLUUID id) { mDefaultImage = id.isNull(); diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h index 59d08d19ed..7f53f9998d 100644 --- a/indra/newview/llinventorygallery.h +++ b/indra/newview/llinventorygallery.h @@ -102,7 +102,7 @@ public: void getCurrentCategories(uuid_vec_t& vcur); bool updateAddedItem(LLUUID item_id); // returns true if added item is visible void updateRemovedItem(LLUUID item_id); - void updateChangedItemName(LLUUID item_id, std::string name); + void updateChangedItemData(LLUUID item_id, std::string name, bool is_favorite); void updateItemThumbnail(LLUUID item_id); void updateWornItem(LLUUID item_id, bool is_worn); @@ -227,7 +227,7 @@ private: bool updateRowsIfNeeded(); void updateGalleryWidth(); - LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn); + LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn, bool is_favorite); LLInventoryGalleryItem* getItem(const LLUUID& id) const; void buildGalleryPanel(int row_count); @@ -343,6 +343,7 @@ public: void setHidden(bool hidden) {mHidden = hidden;} void setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link); + void setFavorite(bool is_favorite); LLAssetType::EType getAssetType() { return mType; } void setThumbnail(LLUUID id); void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; } diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index ec3e03ee2d..7212c4dedb 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -250,6 +250,20 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata) { ungroup_folder_items(mUUIDs.front()); } + else if ("add_to_favorites" == action) + { + for (const LLUUID& id : mUUIDs) + { + set_favorite(id, true); + } + } + else if ("remove_from_favorites" == action) + { + for (const LLUUID& id : mUUIDs) + { + set_favorite(id, false); + } + } else if ("replaceoutfit" == action) { modify_outfit(false, mUUIDs.front(), &gInventory); @@ -472,22 +486,7 @@ void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLS void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata) { const std::string param = userdata.asString(); - if (param == "model") - { - gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString()); - } - else if (param == "texture") - { - gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString()); - } - else if (param == "sound") - { - gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString()); - } - else if (param == "animation") - { - gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString()); - } + LLInventoryAction::fileUploadLocation(mUUIDs.front(), param); } bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata) @@ -768,6 +767,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { items.push_back(std::string("New Folder")); } + items.push_back(std::string("upload_options")); items.push_back(std::string("upload_def")); } @@ -787,6 +787,18 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } } + if (!is_trash && !is_in_trash && gInventory.getRootFolderID() != selected_id) + { + if (get_is_favorite(obj)) + { + items.push_back(std::string("Remove from Favorites")); + } + else + { + items.push_back(std::string("Add to Favorites")); + } + } + items.push_back(std::string("Subfolder Separator")); if (!is_system_folder && !isRootFolder() && !is_outfits) { @@ -832,6 +844,17 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men if(is_agent_inventory) { items.push_back(std::string("Cut")); + if (!is_in_trash) + { + if (get_is_favorite(obj)) + { + items.push_back(std::string("Remove from Favorites")); + } + else + { + items.push_back(std::string("Add to Favorites")); + } + } if (!is_link || !is_cof || !get_is_item_worn(selected_id)) { items.push_back(std::string("Delete")); @@ -968,6 +991,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men } disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("upload_options")); disabled_items.push_back(std::string("upload_def")); disabled_items.push_back(std::string("create_new")); } @@ -1018,6 +1042,15 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men disabled_items.push_back(std::string("Marketplace Move")); } } + + if (get_is_favorite(obj)) + { + items.push_back(std::string("Remove from Favorites")); + } + else if (is_agent_inventory) + { + items.push_back(std::string("Add to Favorites")); + } } hide_context_entries(*menu, items, disabled_items); diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 2e0669fc38..cfa37cc3ee 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -115,7 +115,7 @@ void LLInventoryItemsList::doIdle() { if (mRefreshState == REFRESH_COMPLETE) return; - if (isInVisibleChain() || mForceRefresh ) + if (isInVisibleChain() || mForceRefresh || !getFilterSubString().empty()) { refresh(); diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp new file mode 100644 index 0000000000..028483e134 --- /dev/null +++ b/indra/newview/llinventorylistener.cpp @@ -0,0 +1,309 @@ +/** + * @file llinventorylistener.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventorylistener.h" + +#include "llappearancemgr.h" +#include "llinventoryfunctions.h" +#include "lltransutil.h" +#include "llwearableitemslist.h" +#include "stringize.h" + +LLInventoryListener::LLInventoryListener() + : LLEventAPI("LLInventory", + "API for interactions with viewer Inventory items") +{ + add("getItemsInfo", + "Return information about items or folders defined in [\"item_ids\"]:\n" + "reply will contain [\"items\"] and [\"categories\"] result set keys", + &LLInventoryListener::getItemsInfo, + llsd::map("item_ids", LLSD(), "reply", LLSD())); + + add("getFolderTypeNames", + "Return the table of folder type names, contained in [\"names\"]\n", + &LLInventoryListener::getFolderTypeNames, + llsd::map("reply", LLSD())); + + add("getAssetTypeNames", + "Return the table of asset type names, contained in [\"names\"]\n", + &LLInventoryListener::getAssetTypeNames, + llsd::map("reply", LLSD())); + + add("getBasicFolderID", + "Return the UUID of the folder by specified folder type name, for example:\n" + "\"Textures\", \"My outfits\", \"Sounds\" and other basic folders which have associated type", + &LLInventoryListener::getBasicFolderID, + llsd::map("ft_name", LLSD(), "reply", LLSD())); + + add("getDirectDescendants", + "Return result set keys [\"categories\"] and [\"items\"] for the direct\n" + "descendants of the [\"folder_id\"]", + &LLInventoryListener::getDirectDescendants, + llsd::map("folder_id", LLSD(), "reply", LLSD())); + + add("collectDescendantsIf", + "Return result set keys [\"categories\"] and [\"items\"] for the descendants\n" + "of the [\"folder_id\"], if it passes specified filters:\n" + "[\"name\"] is a substring of object's name,\n" + "[\"desc\"] is a substring of object's description,\n" + "asset [\"type\"] corresponds to the string name of the object's asset type\n" + "[\"limit\"] sets item count limit in result set (default unlimited)\n" + "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", + &LLInventoryListener::collectDescendantsIf, + llsd::map("folder_id", LLSD(), "reply", LLSD())); +} + +void add_cat_info(LLEventAPI::Response& response, LLViewerInventoryCategory* cat) +{ + response["categories"].insert(cat->getUUID().asString(), + llsd::map("id", cat->getUUID(), + "name", cat->getName(), + "parent_id", cat->getParentUUID(), + "type", LLFolderType::lookup(cat->getPreferredType()))); + +}; + +void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) +{ + response["items"].insert(item->getUUID().asString(), + llsd::map("id", item->getUUID(), + "name", item->getName(), + "parent_id", item->getParentUUID(), + "desc", item->getDescription(), + "inv_type", LLInventoryType::lookup(item->getInventoryType()), + "asset_type", LLAssetType::lookup(item->getType()), + "creation_date", LLSD::Integer(item->getCreationDate()), + "asset_id", item->getAssetUUID(), + "is_link", item->getIsLinkType(), + "linked_id", item->getLinkedUUID())); +} + +void add_objects_info(LLEventAPI::Response& response, LLInventoryModel::cat_array_t cat_array, LLInventoryModel::item_array_t item_array) +{ + for (auto& p : item_array) + { + add_item_info(response, p); + } + for (auto& p : cat_array) + { + add_cat_info(response, p); + } +} + +void LLInventoryListener::getItemsInfo(LLSD const &data) +{ + Response response(LLSD(), data); + uuid_vec_t ids = LLSDParam(data["item_ids"]); + for (auto &it : ids) + { + LLViewerInventoryItem* item = gInventory.getItem(it); + if (item) + { + add_item_info(response, item); + } + else + { + LLViewerInventoryCategory *cat = gInventory.getCategory(it); + if (cat) + { + add_cat_info(response, cat); + } + } + } +} + +void LLInventoryListener::getFolderTypeNames(LLSD const &data) +{ + Response response(llsd::map("names", LLFolderType::getTypeNames()), data); +} + +void LLInventoryListener::getAssetTypeNames(LLSD const &data) +{ + Response response(llsd::map("names", LLAssetType::getTypeNames()), data); +} + +void LLInventoryListener::getBasicFolderID(LLSD const &data) +{ + Response response(llsd::map("id", gInventory.findCategoryUUIDForType(LLFolderType::lookup(data["ft_name"].asString()))), data); +} + + +void LLInventoryListener::getDirectDescendants(LLSD const &data) +{ + Response response(LLSD(), data); + LLUUID folder_id(data["folder_id"].asUUID()); + LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (!cat) + { + return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); + } + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, cats, items); + + add_objects_info(response, *cats, *items); +} + +struct LLFilteredCollector : public LLInventoryCollectFunctor +{ + enum EFilterLink + { + INCLUDE_LINKS, // show links too + EXCLUDE_LINKS, // don't show links + ONLY_LINKS // only show links + }; + + LLFilteredCollector(LLSD const &data); + virtual ~LLFilteredCollector() {} + virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item) override; + virtual bool exceedsLimit() override + { + // mItemLimit == 0 means unlimited + return (mItemLimit && mItemLimit <= mItemCount); + } + + protected: + bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); + + LLAssetType::EType mType; + std::string mName; + std::string mDesc; + EFilterLink mLinkFilter; + + S32 mItemLimit; + S32 mItemCount; +}; + +void LLInventoryListener::collectDescendantsIf(LLSD const &data) +{ + Response response(LLSD(), data); + LLUUID folder_id(data["folder_id"].asUUID()); + LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (!cat) + { + return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); + } + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLFilteredCollector collector = LLFilteredCollector(data); + + gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); + + add_objects_info(response, cat_array, item_array); +} + +LLFilteredCollector::LLFilteredCollector(LLSD const &data) : + mType(LLAssetType::EType::AT_UNKNOWN), + mLinkFilter(INCLUDE_LINKS), + mItemLimit(0), + mItemCount(0) +{ + + mName = data["name"].asString(); + mDesc = data["desc"].asString(); + + if (data.has("type")) + { + mType = LLAssetType::lookup(data["type"]); + } + if (data.has("filter_links")) + { + if (data["filter_links"] == "EXCLUDE_LINKS") + { + mLinkFilter = EXCLUDE_LINKS; + } + else if (data["filter_links"] == "ONLY_LINKS") + { + mLinkFilter = ONLY_LINKS; + } + } + if (data["limit"].isInteger()) + { + mItemLimit = std::max(data["limit"].asInteger(), 1); + } +} + +bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool passed = checkagainstType(cat, item); + passed = passed && checkagainstNameDesc(cat, item); + passed = passed && checkagainstLinks(cat, item); + + if (passed) + { + ++mItemCount; + } + return passed; +} + +bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) +{ + std::string name, desc; + bool passed(true); + if (cat) + { + if (!mDesc.empty()) return false; + name = cat->getName(); + } + if (item) + { + name = item->getName(); + passed = (mDesc.empty() || (item->getDescription().find(mDesc) != std::string::npos)); + } + + return passed && (mName.empty() || name.find(mName) != std::string::npos); +} + +bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item) +{ + if (mType == LLAssetType::AT_UNKNOWN) + { + return true; + } + if (cat && (mType == LLAssetType::AT_CATEGORY)) + { + return true; + } + if (item && item->getType() == mType) + { + return true; + } + return false; +} + +bool LLFilteredCollector::checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool is_link = cat ? cat->getIsLinkType() : item->getIsLinkType(); + if (is_link && (mLinkFilter == EXCLUDE_LINKS)) + return false; + if (!is_link && (mLinkFilter == ONLY_LINKS)) + return false; + return true; +} diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h new file mode 100644 index 0000000000..d50397730c --- /dev/null +++ b/indra/newview/llinventorylistener.h @@ -0,0 +1,48 @@ +/** + * @file llinventorylistener.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLINVENTORYLISTENER_H +#define LL_LLINVENTORYLISTENER_H + +#include "lleventapi.h" +#include "llinventoryfunctions.h" + +class LLInventoryListener : public LLEventAPI +{ +public: + LLInventoryListener(); + +private: + void getItemsInfo(LLSD const &data); + void getFolderTypeNames(LLSD const &data); + void getAssetTypeNames(LLSD const &data); + void getBasicFolderID(LLSD const &data); + void getDirectDescendants(LLSD const &data); + void collectDescendantsIf(LLSD const &data); +}; + +#endif // LL_LLINVENTORYLISTENER_H + diff --git a/indra/newview/llinventorylistitem.cpp b/indra/newview/llinventorylistitem.cpp index e210975a5a..5fb5b0f23f 100644 --- a/indra/newview/llinventorylistitem.cpp +++ b/indra/newview/llinventorylistitem.cpp @@ -43,7 +43,19 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelInventoryListItemBaseParams(&typeid(LLPanelInventoryListItemBase::Params), "inventory_list_item"); -static const S32 WIDGET_SPACING = 3; +constexpr S32 WIDGET_SPACING = 3; +constexpr S32 FAVORITE_IMAGE_SIZE = 14; +constexpr S32 FAVORITE_IMAGE_PAD = 3; + +bool get_is_item_favorite(const LLViewerInventoryItem* inv) +{ + if (inv->getIsLinkType()) + { + LLInventoryObject* obj = gInventory.getObject(inv->getLinkedUUID()); + return obj && obj->getIsFavorite(); + } + return inv->getIsFavorite(); +} LLPanelInventoryListItemBase::Params::Params() : default_style("default_style"), @@ -75,19 +87,30 @@ void LLPanelInventoryListItemBase::draw() LLViewerInventoryItem* inv_item = getItem(); if (inv_item) { - updateItem(inv_item->getName()); + updateItem(inv_item->getName(), get_is_item_favorite(inv_item)); } setNeedsRefresh(false); } + static LLUICachedControl draw_star("InventoryFavoritesUseStar", true); + + LLRect local_rect = getLocalRect(); if (mHovered && mHoverImage) { - mHoverImage->draw(getLocalRect()); + mHoverImage->draw(local_rect); + } + else if (mIsFavorite && draw_star()) + { + + static LLPointer fav_img = LLRender2D::getInstance()->getUIImage("Inv_Favorite_Star_Full"); + gl_draw_scaled_image( + local_rect.getWidth() - FAVORITE_IMAGE_SIZE - FAVORITE_IMAGE_PAD, FAVORITE_IMAGE_PAD, + FAVORITE_IMAGE_SIZE, FAVORITE_IMAGE_SIZE, fav_img->getImage()); } if (mSelected && mSelectedImage) { - mSelectedImage->draw(getLocalRect()); + mSelectedImage->draw(local_rect); } if (mSeparatorVisible && mSeparatorImage) @@ -95,7 +118,7 @@ void LLPanelInventoryListItemBase::draw() // place under bottom of listitem, using image height // item_pad in list using the item should be >= image height // to avoid cropping of top of the next item. - LLRect separator_rect = getLocalRect(); + LLRect separator_rect = local_rect; separator_rect.mTop = separator_rect.mBottom; separator_rect.mBottom -= mSeparatorImage->getHeight(); F32 alpha = getCurrentTransparency(); @@ -107,9 +130,15 @@ void LLPanelInventoryListItemBase::draw() // virtual void LLPanelInventoryListItemBase::updateItem(const std::string& name, + bool favorite, EItemState item_state) { setIconImage(mIconImage); + if (mIsFavorite != favorite) + { + mIsFavorite = favorite; + reshapeMiddleWidgets(); + } setTitle(name, mHighlightedText, item_state); } @@ -164,7 +193,7 @@ bool LLPanelInventoryListItemBase::postBuild() if (inv_item) { mIconImage = LLInventoryIcon::getIcon(inv_item->getType(), inv_item->getInventoryType(), inv_item->getFlags(), false); - updateItem(inv_item->getName()); + updateItem(inv_item->getName(), get_is_item_favorite(inv_item)); } setNeedsRefresh(true); @@ -290,6 +319,7 @@ LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem mHovered(false), mSelected(false), mSeparatorVisible(false), + mIsFavorite(false), mHoverImage(params.hover_image), mSelectedImage(params.selected_image), mSeparatorImage(params.separator_image) @@ -392,6 +422,16 @@ void LLPanelInventoryListItemBase::setTitle(const std::string& title, default:; } + if (mIsFavorite) + { + static LLUICachedControl use_color("InventoryFavoritesColorText", true); + if (use_color) + { + static const LLUIColor favorite_color = LLUIColorTable::instance().getColor("InventoryFavoriteColor", LLColor4::white); + style_params.color = favorite_color; + } + } + LLTextUtil::textboxSetHighlightedVal( mTitleCtrl, style_params, @@ -466,6 +506,10 @@ void LLPanelInventoryListItemBase::reshapeMiddleWidgets() S32 name_left = icon_rect.mRight + getWidgetSpacing(); S32 name_right = getLocalRect().getWidth() - mRightWidgetsWidth - getWidgetSpacing(); + if (mIsFavorite) + { + name_right -= FAVORITE_IMAGE_SIZE + FAVORITE_IMAGE_PAD; + } LLRect name_rect(mTitleCtrl->getRect()); name_rect.set(name_left, name_rect.mTop, name_right, name_rect.mBottom); mTitleCtrl->setShape(name_rect); diff --git a/indra/newview/llinventorylistitem.h b/indra/newview/llinventorylistitem.h index 21540a380b..40a86001a4 100644 --- a/indra/newview/llinventorylistitem.h +++ b/indra/newview/llinventorylistitem.h @@ -167,6 +167,7 @@ protected: * Called after inventory item was updated, update panel widgets to reflect inventory changes. */ virtual void updateItem(const std::string& name, + bool favorite, EItemState item_state = IS_DEFAULT); void setLeftWidgetsWidth(S32 width) { mLeftWidgetsWidth = width; } @@ -222,8 +223,9 @@ private: LLUIImagePtr mSelectedImage; LLUIImagePtr mSeparatorImage; - bool mSelected; - bool mSeparatorVisible; + bool mSelected = false; + bool mSeparatorVisible = false; + bool mIsFavorite = false; // note that any setter needs to update tittle std::string mHighlightedText; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index f1ca69416b..2dfc3b014f 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -78,7 +78,7 @@ // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect. -const S32 LLInventoryModel::sCurrentInvCacheVersion = 3; +const S32 LLInventoryModel::sCurrentInvCacheVersion = 5; bool LLInventoryModel::sFirstTimeInViewer2 = true; S32 LLInventoryModel::sPendingSystemFolders = 0; @@ -1283,6 +1283,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, { for (auto& cat : *cat_array) { + if (add.exceedsLimit()) + { + break; + } if(add(cat,NULL)) { cats.push_back(cat); @@ -1298,6 +1302,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, { for (auto& item : *item_array) { + if (add.exceedsLimit()) + { + break; + } if(add(NULL, item)) { items.push_back(item); @@ -2809,8 +2817,9 @@ bool LLInventoryModel::loadSkeleton( cached_ids.insert(tcat->getUUID()); // At the moment download does not provide a thumbnail - // uuid, use the one from cache + // uuid or favorite, use values from cache tcat->setThumbnailUUID(cat->getThumbnailUUID()); + tcat->setFavorite(cat->getIsFavorite()); } } @@ -3375,7 +3384,7 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } LL_INFOS(LOG_INV) << "loading inventory from: (" << filename << ")" << LL_ENDL; - llifstream file(filename.c_str()); + llifstream file(filename.c_str(), std::ifstream::in | std::ifstream::binary); if (!file.is_open()) { @@ -3384,80 +3393,92 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } is_cache_obsolete = true; // Obsolete until proven current - - //U64 lines_count = 0U; - std::string line; - LLPointer parser = new LLSDNotationParser(); - while (std::getline(file, line)) + U32 value_nbo = 0; + file.read((char*)&value_nbo, sizeof(U32)); + if (file.fail()) { - LLSD s_item; - std::istringstream iss(line); - if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + LL_WARNS(LOG_INV) << "Failed to read cache version. Unable to load inventory from: " << filename << LL_ENDL; + } + else + { + S32 version = (S32)ntohl(value_nbo); + if (version == sCurrentInvCacheVersion) { - LL_WARNS(LOG_INV)<< "Parsing inventory cache failed" << LL_ENDL; - break; + // Cache is up to date + is_cache_obsolete = false; } - - if (s_item.has("inv_cache_version")) + else { - S32 version = s_item["inv_cache_version"].asInteger(); - if (version == sCurrentInvCacheVersion) - { - // Cache is up to date - is_cache_obsolete = false; - continue; - } - else - { - LL_WARNS(LOG_INV)<< "Inventory cache is out of date" << LL_ENDL; - break; - } + LL_WARNS(LOG_INV) << "Inventory cache is out of date" << LL_ENDL; } - else if (s_item.has("cat_id")) - { - if (is_cache_obsolete) - break; + } - LLPointer inv_cat = new LLViewerInventoryCategory(LLUUID::null); - if(inv_cat->importLLSD(s_item)) - { - categories.push_back(inv_cat); - } + LLSD inventory; + if (!is_cache_obsolete) + { + LLPointer parser = new LLSDBinaryParser(); + + if (parser->parse(file, inventory, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) + { + is_cache_obsolete = true; + LL_WARNS(LOG_INV) << "Parsing inventory cache failed" << LL_ENDL; } - else if (s_item.has("item_id")) - { - if (is_cache_obsolete) - break; + } - LLPointer inv_item = new LLViewerInventoryItem; - if( inv_item->fromLLSD(s_item) ) + if (!is_cache_obsolete) + { + const LLSD& llsd_cats = inventory["categories"]; + if (llsd_cats.isArray()) + { + LLSD::array_const_iterator iter = llsd_cats.beginArray(); + LLSD::array_const_iterator end = llsd_cats.endArray(); + for (; iter != end; ++iter) { - if(inv_item->getUUID().isNull()) + LLPointer inv_cat = new LLViewerInventoryCategory(LLUUID::null); + if (inv_cat->importLLSDMap(*iter)) { - LL_DEBUGS(LOG_INV) << "Ignoring inventory with null item id: " - << inv_item->getName() << LL_ENDL; + categories.push_back(inv_cat); } - else + } + } + + const LLSD& llsd_items = inventory["items"]; + if (llsd_items.isArray()) + { + LLSD::array_const_iterator iter = llsd_items.beginArray(); + LLSD::array_const_iterator end = llsd_items.endArray(); + for (; iter != end; ++iter) + { + LLPointer inv_item = new LLViewerInventoryItem; + if (inv_item->fromLLSD(*iter)) { - if (inv_item->getType() == LLAssetType::AT_UNKNOWN) + if (inv_item->getUUID().isNull()) { - cats_to_update.insert(inv_item->getParentUUID()); + LL_DEBUGS(LOG_INV) << "Ignoring inventory with null item id: " + << inv_item->getName() << LL_ENDL; } else { - items.push_back(inv_item); + if (inv_item->getType() == LLAssetType::AT_UNKNOWN) + { + cats_to_update.insert(inv_item->getParentUUID()); + } + else + { + items.push_back(inv_item); + } } } + + // TODO(brad) - figure out how to reenable this without breaking everything else + // static constexpr U64 BATCH_SIZE = 512U; + // if ((++lines_count % BATCH_SIZE) == 0) + // { + // // SL-19968 - make sure message system code gets a chance to run every so often + // pump_idle_startup_network(); + // } } } - -// TODO(brad) - figure out how to reenable this without breaking everything else -// static constexpr U64 BATCH_SIZE = 512U; -// if ((++lines_count % BATCH_SIZE) == 0) -// { -// // SL-19968 - make sure message system code gets a chance to run every so often -// pump_idle_startup_network(); -// } } file.close(); @@ -3480,54 +3501,54 @@ bool LLInventoryModel::saveToFile(const std::string& filename, try { - llofstream fileXML(filename.c_str()); - if (!fileXML.is_open()) + llofstream fileSD(filename.c_str(), std::ios_base::out | std::ios_base::binary); + if (!fileSD.is_open()) { LL_WARNS(LOG_INV) << "Failed to open file. Unable to save inventory to: " << filename << LL_ENDL; return false; } - - LLSD cache_ver; - cache_ver["inv_cache_version"] = sCurrentInvCacheVersion; - - if (fileXML.fail()) + U32 value_nbo = htonl(sCurrentInvCacheVersion); + fileSD.write((const char*)(&value_nbo), sizeof(U32)); + if (fileSD.fail()) { - LL_WARNS(LOG_INV) << "Failed to write cache version to file. Unable to save inventory to: " << filename << LL_ENDL; + LL_WARNS(LOG_INV) << "Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL; return false; } - fileXML << LLSDOStreamer(cache_ver) << std::endl; + LLSD inventory; + inventory["categories"] = LLSD::emptyArray(); + LLSD& cat_array = inventory["categories"]; S32 cat_count = 0; for (auto& cat : categories) { if (cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { - fileXML << LLSDOStreamer(cat->exportLLSD()) << std::endl; + LLSD sd; + cat->exportLLSD(sd); + cat_array.append(sd); cat_count++; } - - if (fileXML.fail()) - { - LL_WARNS(LOG_INV) << "Failed to write a folder to file. Unable to save inventory to: " << filename << LL_ENDL; - return false; - } } + inventory["items"] = LLSD::emptyArray(); + LLSD& item_array = inventory["items"]; auto it_count = items.size(); for (auto& item : items) { - fileXML << LLSDOStreamer(item->asLLSD()) << std::endl; - - if (fileXML.fail()) - { - LL_WARNS(LOG_INV) << "Failed to write an item to file. Unable to save inventory to: " << filename << LL_ENDL; - return false; - } + LLSD sd; + item->asLLSD(sd); + item_array.append(sd); } - fileXML.flush(); + fileSD << LLSDOStreamer(inventory) << std::endl; + if (fileSD.fail()) + { + LL_WARNS(LOG_INV) << "Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL; + return false; + } + fileSD.flush(); - fileXML.close(); + fileSD.close(); LL_INFOS(LOG_INV) << "Inventory saved: " << (S32)cat_count << " categories, " << (S32)it_count << " items." << LL_ENDL; } diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index ac791e224e..ac22be9d5a 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -749,6 +749,13 @@ void LLInventoryCategoriesObserver::changed(U32 mask) cat_changed = true; } + bool is_favorite = category->getIsFavorite(); + if (cat_data.mIsFavorite != is_favorite) + { + cat_data.mIsFavorite = is_favorite; + cat_changed = true; + } + // If anything has changed above, fire the callback. if (cat_changed) cat_data.mCallback(); @@ -766,6 +773,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN; S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN; bool can_be_added = true; + bool favorite = false; LLUUID thumbnail_id; LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); @@ -779,6 +787,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t // to a category have been made. version = category->getVersion(); thumbnail_id = category->getThumbnailUUID(); + favorite = category->getIsFavorite(); LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; @@ -804,11 +813,11 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t if(init_name_hash) { digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id); - mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents,item_name_hash))); + mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, favorite, cb, version, current_num_known_descendents,item_name_hash))); } else { - mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents))); + mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, favorite, cb, version, current_num_known_descendents))); } } @@ -821,25 +830,37 @@ void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id) } LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( - const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents) + const LLUUID& cat_id, + const LLUUID& thumbnail_id, + bool is_favorite, + callback_t cb, + S32 version, + S32 num_descendents) : mCatID(cat_id) , mCallback(cb) , mVersion(version) , mDescendentsCount(num_descendents) , mThumbnailId(thumbnail_id) + , mIsFavorite(is_favorite) , mIsNameHashInitialized(false) { } LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( - const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash) + const LLUUID& cat_id, + const LLUUID& thumbnail_id, + bool is_favorite, + callback_t cb, S32 version, + S32 num_descendents, + const digest_t& name_hash) : mCatID(cat_id) , mCallback(cb) , mVersion(version) , mDescendentsCount(num_descendents) , mThumbnailId(thumbnail_id) + , mIsFavorite(is_favorite) , mIsNameHashInitialized(true) , mItemNameHash(name_hash) { diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 950b02d3cf..12d6c44521 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -60,6 +60,7 @@ public: CREATE = 512, // With ADD, item has just been created. // unfortunately a particular message is still associated with some unique semantics. UPDATE_CREATE = 1024, // With ADD, item added via UpdateCreateInventoryItem + UPDATE_FAVORITE = 2048, // With ADD, item added via UpdateCreateInventoryItem ALL = 0xffffffff }; LLInventoryObserver(); @@ -276,12 +277,26 @@ protected: typedef LLUUID digest_t; // To clarify the actual usage of this "UUID" struct LLCategoryData { - LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents); - LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash); + LLCategoryData( + const LLUUID& cat_id, + const LLUUID& thumbnail_id, + bool is_favorite, + callback_t cb, + S32 version, + S32 num_descendents); + LLCategoryData( + const LLUUID& cat_id, + const LLUUID& thumbnail_id, + bool is_favorite, + callback_t cb, + S32 version, + S32 num_descendents, + const digest_t& name_hash); callback_t mCallback; S32 mVersion; S32 mDescendentsCount; digest_t mItemNameHash; + bool mIsFavorite; bool mIsNameHashInitialized; LLUUID mCatID; LLUUID mThumbnailId; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 443a0022fb..b540e9c5bb 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -55,11 +55,13 @@ #include "llviewerfoldertype.h" #include "llvoavatarself.h" +class LLInventoryFavoritesItemsPanel; class LLInventoryRecentItemsPanel; class LLAssetFilteredInventoryPanel; static LLDefaultChildRegistry::Register r("inventory_panel"); static LLDefaultChildRegistry::Register t_recent_inventory_panel("recent_inventory_panel"); +static LLDefaultChildRegistry::Register t_favorites_inventory_panel("favorites_inventory_panel"); static LLDefaultChildRegistry::Register t_asset_filtered_inv_panel("asset_filtered_inv_panel"); const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); @@ -641,6 +643,19 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve } } + if (mask & LLInventoryObserver::UPDATE_FAVORITE) + { + if (view_item) + { + view_item->refresh(); + LLFolderViewFolder* parent = view_item->getParentFolder(); + if (parent) + { + parent->updateHasFavorites(get_is_favorite(model_item)); + } + } + } + // We don't typically care which of these masks the item is actually flagged with, since the masks // may not be accurate (e.g. in the main inventory panel, I move an item from My Inventory into // Landmarks; this is a STRUCTURE change for that panel but is an ADD change for the Landmarks @@ -669,6 +684,16 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve setSelection(item_id, false); } updateFolderLabel(model_item->getParentUUID()); + + if (get_is_favorite(model_item)) + { + LLFolderViewFolder* new_parent = getFolderByID(model_item->getParentUUID()); + if (new_parent) + { + new_parent->updateHasFavorites(true); + } + } + } ////////////////////////////// @@ -682,9 +707,11 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve { LLFolderViewModelItem* old_parent_vmi = old_parent->getViewModelItem(); LLFolderViewModelItemInventory* viewmodel_folder = static_cast(old_parent_vmi); - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); - // Item has been moved. - if (old_parent != new_parent) + LLFolderViewFolder* new_parent = getFolderByID(model_item->getParentUUID()); + + if (old_parent != new_parent // Item has been moved. + && (new_parent != NULL || !isInRootContent(item_id, view_item)) // item is not or shouldn't be in root content + ) { if (new_parent != NULL) { @@ -719,9 +746,21 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve { old_parent_vmi->dirtyDescendantsFilter(); } + + if (view_item->isFavorite()) + { + if (old_parent) + { + old_parent->updateHasFavorites(false); // favorite was removed + } + if (new_parent) + { + new_parent->updateHasFavorites(true); // favorite was added + } } } } + } ////////////////////////////// // REMOVE Operation @@ -744,6 +783,10 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve updateFolderLabel(viewmodel_folder->getUUID()); } } + if (view_item->isFavorite()) + { + parent->updateHasFavorites(false); // favorite was removed + } } } } @@ -754,7 +797,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { LL_PROFILE_ZONE_SCOPED; - if (mViewsInitialized != VIEWS_INITIALIZED) return; + if (mViewsInitialized != VIEWS_INITIALIZED) return; // todo: Store changes if building? const LLInventoryModel* model = getModel(); if (!model) return; @@ -861,7 +904,23 @@ void LLInventoryPanel::idle(void* user_data) bool in_visible_chain = panel->isInVisibleChain(); - if (!panel->mBuildViewsQueue.empty()) + if (!panel->mBuildRootQueue.empty()) + { + const F64 max_time = in_visible_chain ? 0.006f : 0.001f; // 6 ms + F64 curent_time = LLTimer::getTotalSeconds(); + panel->mBuildViewsEndTime = curent_time + max_time; + + while (curent_time < panel->mBuildViewsEndTime + && !panel->mBuildRootQueue.empty()) + { + LLUUID item_id = panel->mBuildRootQueue.back(); + panel->mBuildRootQueue.pop_back(); + panel->findAndInitRootContent(item_id); + + curent_time = LLTimer::getTotalSeconds(); + } + } + else if (!panel->mBuildViewsQueue.empty()) { const F64 max_time = in_visible_chain ? 0.006f : 0.001f; // 6 ms F64 curent_time = LLTimer::getTotalSeconds(); @@ -901,6 +960,11 @@ void LLInventoryPanel::idle(void* user_data) panel->mViewsInitialized = VIEWS_INITIALIZED; } } + // in case panel is empty or only has 'roots' + else if (panel->mViewsInitialized == VIEWS_BUILDING) + { + panel->mViewsInitialized = VIEWS_INITIALIZED; + } // Take into account the fact that the root folder might be invalidated if (panel->mFolderRoot.get()) @@ -943,20 +1007,9 @@ void LLInventoryPanel::initializeViews(F64 max_time) mBuildViewsEndTime = curent_time + max_time; // init everything - LLUUID root_id = getRootFolderID(); - if (root_id.notNull()) - { - buildNewViews(getRootFolderID()); - } - else - { - // Default case: always add "My Inventory" root first, "Library" root second - // If we run out of time, this still should create root folders - buildNewViews(gInventory.getRootFolderID()); // My Inventory - buildNewViews(gInventory.getLibraryRootFolderID()); // Library - } + initRootContent(); - if (mBuildViewsQueue.empty()) + if (mBuildViewsQueue.empty() && mBuildRootQueue.empty()) { mViewsInitialized = VIEWS_INITIALIZED; } @@ -987,6 +1040,22 @@ void LLInventoryPanel::initializeViews(F64 max_time) } } +void LLInventoryPanel::initRootContent() +{ + LLUUID root_id = getRootFolderID(); + if (root_id.notNull()) + { + buildNewViews(getRootFolderID()); + } + else + { + // Default case: always add "My Inventory" root first, "Library" root second + // If we run out of time, this still should create root folders + buildNewViews(gInventory.getRootFolderID()); // My Inventory + buildNewViews(gInventory.getLibraryRootFolderID()); // Library + } +} + LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop) { @@ -1758,26 +1827,8 @@ bool LLInventoryPanel::beginIMSession() void LLInventoryPanel::fileUploadLocation(const LLSD& userdata) { const std::string param = userdata.asString(); - if (param == "model") - { - gSavedPerAccountSettings.setString("ModelUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString()); - } - else if (param == "texture") - { - gSavedPerAccountSettings.setString("TextureUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString()); - } - else if (param == "sound") - { - gSavedPerAccountSettings.setString("SoundUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString()); - } - else if (param == "animation") - { - gSavedPerAccountSettings.setString("AnimationUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString()); - } - else if (param == "pbr_material") - { - gSavedPerAccountSettings.setString("PBRUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString()); - } + const LLUUID dest = LLFolderBridge::sSelf.get()->getUUID(); + LLInventoryAction::fileUploadLocation(dest, param); } void LLInventoryPanel::openSingleViewInventory(LLUUID folder_id) @@ -2225,6 +2276,274 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params) mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } +/************************************************************************/ +/* Favorites Inventory Panel related class */ +/************************************************************************/ +static const LLFavoritesInventoryBridgeBuilder FAVORITES_BUILDER; +class LLInventoryFavoritesItemsPanel : public LLInventoryPanel +{ +public: + struct Params : public LLInitParam::Block + {}; + + void initFromParams(const Params& p) + { + LLInventoryPanel::initFromParams(p); + // turn off trash + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() | (1ULL << LLFolderType::FT_TRASH)); + getFilter().setFilterNoTrashFolder(); + // turn off marketplace for favorites + getFilter().setFilterNoMarketplaceFolder(); + } + + void removeItemID(const LLUUID& id) override; + bool isInRootContent(const LLUUID& id, LLFolderViewItem* view_item) override; + +protected: + LLInventoryFavoritesItemsPanel(const Params&); + friend class LLUICtrlFactory; + + void findAndInitRootContent(const LLUUID& folder_id) override; + void initRootContent() override; + + // removeFavorite removes item from root, does not readd favorited children if present + bool removeFavorite(const LLUUID& id, const LLInventoryObject* model_item); + void itemChanged(const LLUUID& item_id, U32 mask, const LLInventoryObject* model_item) override; + + std::set mRootContentIDs; +}; + +LLInventoryFavoritesItemsPanel::LLInventoryFavoritesItemsPanel(const Params& params) + : LLInventoryPanel(params) +{ + // replace bridge builder to have necessary View bridges. + mInvFVBridgeBuilder = &FAVORITES_BUILDER; +} + +void LLInventoryFavoritesItemsPanel::removeItemID(const LLUUID& id) +{ + std::set::iterator found = mRootContentIDs.find(id); + if (found != mRootContentIDs.end()) + { + mRootContentIDs.erase(found); + // check content for favorites + mBuildRootQueue.emplace_back(id); + } + + LLInventoryPanel::removeItemID(id); +} + +bool LLInventoryFavoritesItemsPanel::isInRootContent(const LLUUID& id, LLFolderViewItem* view_item) +{ + if (!view_item->isFavorite()) + { + return false; + } + + std::set::iterator found = mRootContentIDs.find(id); + return found != mRootContentIDs.end(); +} + +void LLInventoryFavoritesItemsPanel::findAndInitRootContent(const LLUUID& id) +{ + F64 curent_time = LLTimer::getTotalSeconds(); + if (mBuildViewsEndTime < curent_time) + { + mBuildRootQueue.emplace_back(id); + return; + } + LLViewerInventoryCategory::cat_array_t* categories; + LLViewerInventoryItem::item_array_t* items; + mInventory->lockDirectDescendentArrays(id, categories, items); + + if (categories) + { + S32 count = static_cast(categories->size()); + for (S32 i = 0; i < count; ++i) + { + LLViewerInventoryCategory* cat = categories->at(i); + if (cat->getPreferredType() == LLFolderType::FT_TRASH) + { + continue; + } + else if (cat->getIsFavorite()) + { + LLFolderViewItem* folder_view_item = getItemByID(cat->getUUID()); + if (!folder_view_item) + { + const LLUUID& parent_id = cat->getParentUUID(); + mRootContentIDs.emplace(cat->getUUID()); + + buildViewsTree(cat->getUUID(), parent_id, cat, folder_view_item, mFolderRoot.get(), BUILD_TIMELIMIT); + } + } + else + { + findAndInitRootContent(cat->getUUID()); + } + } + } + + if (items) + { + S32 count = static_cast(items->size()); + for (S32 i = 0; i < count; ++i) + { + LLViewerInventoryItem* item = items->at(i); + const LLUUID item_id = item->getUUID(); + if (item->getIsFavorite() && typedViewsFilter(item_id, item)) + { + LLFolderViewItem* folder_view_item = getItemByID(id); + if (!folder_view_item) + { + const LLUUID& parent_id = item->getParentUUID(); + mRootContentIDs.emplace(item_id); + + buildViewsTree(item_id, parent_id, item, folder_view_item, mFolderRoot.get(), BUILD_TIMELIMIT); + } + } + } + } + + mInventory->unlockDirectDescendentArrays(id); +} + +void LLInventoryFavoritesItemsPanel::initRootContent() +{ + findAndInitRootContent(gInventory.getRootFolderID()); // My Inventory +} + +bool LLInventoryFavoritesItemsPanel::removeFavorite(const LLUUID& id, const LLInventoryObject* model_item) +{ + std::set::iterator found = mRootContentIDs.find(id); + if (found == mRootContentIDs.end()) + { + return false; + } + + mRootContentIDs.erase(found); + + // This item is in root's content, remove item's UI. + LLFolderViewItem* view_item = getItemByID(id); + if (view_item) + { + LLFolderViewFolder* parent = view_item->getParentFolder(); + LLFolderViewModelItemInventory* viewmodel_item = static_cast(view_item->getViewModelItem()); + if (viewmodel_item) + { + removeItemID(viewmodel_item->getUUID()); + } + view_item->destroyView(); + if (parent) + { + parent->getViewModelItem()->dirtyDescendantsFilter(); + LLFolderViewModelItemInventory* viewmodel_folder = static_cast(parent->getViewModelItem()); + if (viewmodel_folder) + { + updateFolderLabel(viewmodel_folder->getUUID()); + } + if (view_item->isFavorite()) + { + parent->updateHasFavorites(false); // favorite was removed + } + } + } + + return true; +} + +void LLInventoryFavoritesItemsPanel::itemChanged(const LLUUID& id, U32 mask, const LLInventoryObject* model_item) +{ + LLFolderViewItem* view_item = getItemByID(id); + if (!model_item && !view_item) + { + // remove operation, but item is not in panel already + return; + } + + bool handled = false; + + if (mask & (LLInventoryObserver::UPDATE_FAVORITE | + LLInventoryObserver::STRUCTURE | + LLInventoryObserver::ADD | + LLInventoryObserver::REMOVE)) + { + // specifically exlude links and not get_is_favorite(model_item) + if (model_item && model_item->getIsFavorite()) + { + if (!view_item) + { + const LLViewerInventoryCategory* cat = dynamic_cast(model_item); + if (cat) + { + // New favorite folder + if (cat->getPreferredType() != LLFolderType::FT_TRASH) + { + // If any descendants were in the list, remove them + // Todo: Consider implementing and checking hasFavorites to save on search + LLFavoritesCollector is_favorite; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendentsIf(id, cat_array, item_array, false, is_favorite); + for (LLInventoryModel::cat_array_t::const_iterator it = cat_array.begin(); it != cat_array.end(); ++it) + { + removeFavorite((*it)->getUUID(), *it); + } + for (LLInventoryModel::item_array_t::const_iterator it = item_array.begin(); it != item_array.end(); ++it) + { + removeFavorite((*it)->getUUID(), *it); + } + + LLFolderViewItem* folder_view_item = getItemByID(cat->getUUID()); + if (!folder_view_item) + { + const LLUUID& parent_id = cat->getParentUUID(); + mRootContentIDs.emplace(cat->getUUID()); + + buildViewsTree(cat->getUUID(), parent_id, cat, folder_view_item, mFolderRoot.get(), BUILD_ONE_FOLDER); + } + } + } + else + { + // New favorite item + if (model_item->getIsFavorite() && typedViewsFilter(id, model_item)) + { + const LLUUID& parent_id = model_item->getParentUUID(); + mRootContentIDs.emplace(id); + + buildViewsTree(id, parent_id, model_item, NULL, mFolderRoot.get(), BUILD_ONE_FOLDER); + } + } + handled = true; + } + } + else + { + handled = removeFavorite(id, model_item); + if (handled) + { + const LLViewerInventoryCategory* cat = dynamic_cast(model_item); + // Todo: Consider implementing and checking hasFavorites to save on search + if (cat) + { + // re-add any favorited children + mBuildRootQueue.emplace_back(id); + } + } + } + } + + if (!handled + && (!model_item || model_item->getParentUUID().notNull())) // filter out 'My inventory' + { + LLInventoryPanel::itemChanged(id, mask, model_item); + } +} +/************************************************************************/ +/* LLInventorySingleFolderPanel */ +/************************************************************************/ + static LLDefaultChildRegistry::Register t_single_folder_inventory_panel("single_folder_inventory_panel"); LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params) diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 56909c8d98..473283352f 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -251,7 +251,8 @@ public: bool reset_filter = false); static void setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id); void addItemID(const LLUUID& id, LLFolderViewItem* itemp); - void removeItemID(const LLUUID& id); + virtual void removeItemID(const LLUUID& id); + virtual bool isInRootContent(const LLUUID& id, LLFolderViewItem* view_item) { return false; } LLFolderViewItem* getItemByID(const LLUUID& id); LLFolderViewFolder* getFolderByID(const LLUUID& id); void setSelectionByID(const LLUUID& obj_id, bool take_keyboard_focus); @@ -334,6 +335,8 @@ public: protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(F64 max_time); + virtual void initRootContent(); + virtual void findAndInitRootContent(const LLUUID& root_id) {}; // Specific inventory colors static bool sColorSetInitialized; @@ -371,7 +374,7 @@ protected: virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); boost::function& items, bool user_action)> mSelectionCallback; -private: + // buildViewsTree does not include some checks and is meant // for recursive use, use buildNewViews() for first call LLFolderViewItem* buildViewsTree(const LLUUID& id, @@ -394,6 +397,8 @@ private: EViewsInitializationState mViewsInitialized; // Whether views have been generated F64 mBuildViewsEndTime; // Stop building views past this timestamp std::deque mBuildViewsQueue; + std::deque mBuildRootQueue; + }; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index fbf4a7d1dd..fa1f650113 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -32,6 +32,8 @@ #include "lllogchat.h" #include "llregex.h" #include "lltrans.h" +#include "llurlaction.h" +#include "llurlentry.h" #include "llviewercontrol.h" #include "lldiriterator.h" @@ -358,13 +360,29 @@ void LLLogChat::saveHistory(const std::string& filename, return; } + std::string altered_line = line; + + // avoid costly regex calls + if (line.find("/mention") != std::string::npos) + { + static const boost::regex mention_regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/mention", boost::regex::perl | boost::regex::icase); + + // replace mention URL with [@username](URL) + altered_line = boost::regex_replace(line, mention_regex, [](const boost::smatch& match) -> std::string + { + std::string url = match[0].str(); + std::string username = LLUrlAction::getURLLabel(url); + return "[" + username + "](" + url + ")"; + }); + } + LLSD item; if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) item["time"] = LLLogChat::timestamp2LogString(0, gSavedPerAccountSettings.getBOOL("LogTimestampDate")); item["from_id"] = from_id; - item["message"] = line; + item["message"] = altered_line; //adding "Second Life:" for all system messages to make chat log history parsing more reliable if (from.empty() && from_id.isNull()) @@ -432,8 +450,8 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list& m } // If we got here, we managed to stat the file. - // Open the file to read - LLFILE* fptr = LLFile::fopen(log_file_name, "r"); /*Flawfinder: ignore*/ + // Open the file to read in binary mode to prevent interpreting other characters as EOF + LLFILE* fptr = LLFile::fopen(log_file_name, "rb"); /*Flawfinder: ignore*/ if (!fptr) { // Ok, this is strange but not really tragic in the big picture of things LL_WARNS("ChatHistory") << "Unable to read file " << log_file_name << " after stat was successful" << LL_ENDL; @@ -470,6 +488,19 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list& m std::string line(remove_utf8_bom(buffer)); + + // fast heuristic test for a mention URL in a string + // this is used to avoid costly regex calls + if (line.find("/mention)") != std::string::npos) + { + // restore original mention URL from [@username](URL) format + static const boost::regex altered_mention_regex("\\[@([^\\]]+)\\]\\((" APP_HEADER_REGEX "/agent/[\\da-f-]+/mention)\\)", + boost::regex::perl | boost::regex::icase); + + // $2 captures the URL part + line = boost::regex_replace(line, altered_mention_regex, "$2"); + } + //updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message if (' ' == line[0]) { @@ -1150,7 +1181,7 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list cb = new LLObjectsMaterialItemCallback(permissions, buffer, name); @@ -1904,7 +1904,11 @@ static void pack_textures( } } -void LLMaterialEditor::uploadMaterialFromModel(const std::string& filename, tinygltf::Model& model_in, S32 index) +void LLMaterialEditor::uploadMaterialFromModel( + const std::string& filename, + tinygltf::Model& model_in, + S32 index, + const LLUUID& dest) { if (index < 0 || !LLMaterialEditor::capabilitiesAvailable()) { @@ -1927,12 +1931,13 @@ void LLMaterialEditor::uploadMaterialFromModel(const std::string& filename, tiny // This uses 'filename' to make sure multiple bulk uploads work // instead of fighting for a single instance. LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor", LLSD().with("filename", filename).with("index", LLSD::Integer(index))); + me->mUploadFolder = dest; me->loadMaterial(model_in, filename, index, false); me->saveIfNeeded(); } -void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 index) +void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 index, const LLUUID& dest_folder) { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -2432,17 +2437,17 @@ void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notificati return; } - createInventoryItem(str.str(), new_name, std::string(), permissions); + createInventoryItem(str.str(), new_name, std::string(), permissions, LLUUID::null); } -const void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type, bool allow_2k); +void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type, bool allow_2k, const LLUUID& dest); void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::string &filename, S32 index, bool open_floater) { if (index == model_in.materials.size()) { // bulk upload all the things - upload_bulk({ filename }, LLFilePicker::FFLOAD_MATERIAL, true); + upload_bulk({ filename }, LLFilePicker::FFLOAD_MATERIAL, true, LLUUID::null); return; } @@ -2872,10 +2877,10 @@ void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const ti } } -void LLMaterialEditor::importMaterial() +void LLMaterialEditor::importMaterial(const LLUUID dest_folder) { LLFilePickerReplyThread::startPicker( - [](const std::vector& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) + [dest_folder](const std::vector& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) { if (LLAppViewer::instance()->quitRequested()) { @@ -2885,7 +2890,7 @@ void LLMaterialEditor::importMaterial() { if (filenames.size() > 0) { - LLMaterialEditor::loadMaterialFromFile(filenames[0], -1); + LLMaterialEditor::loadMaterialFromFile(filenames[0], -1, dest_folder); } } catch (std::bad_alloc&) @@ -3573,6 +3578,7 @@ void LLMaterialEditor::saveTexture(LLImageJ2C* img, const std::string& name, con LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), expected_upload_cost, + mUploadFolder, false, cb, failed_upload)); diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h index 232467460e..1abdd7f84c 100644 --- a/indra/newview/llmaterialeditor.h +++ b/indra/newview/llmaterialeditor.h @@ -94,7 +94,7 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener void setFromGltfMetaData(const std::string& filename, const tinygltf::Model& model, S32 index); // open a file dialog and select a gltf/glb file for import - static void importMaterial(); + static void importMaterial(const LLUUID dest_folder = LLUUID::null); // for live preview, apply current material to currently selected object void applyToSelection(); @@ -105,8 +105,11 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener void loadAsset() override; // @index if -1 and file contains more than one material, // will promt to select specific one - static void uploadMaterialFromModel(const std::string& filename, tinygltf::Model& model, S32 index); - static void loadMaterialFromFile(const std::string& filename, S32 index = -1); + static void uploadMaterialFromModel(const std::string& filename, + tinygltf::Model& model, + S32 index, + const LLUUID& dest_folder_id = LLUUID::null); + static void loadMaterialFromFile(const std::string& filename, S32 index = -1, const LLUUID& dest_folder = LLUUID::null); void onSelectionChanged(); // live overrides selection changes @@ -134,8 +137,6 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener void onClickSave(); - void getGLTFModel(tinygltf::Model& model); - std::string getEncodedAsset(); bool decodeAsset(const std::vector& buffer); @@ -239,7 +240,7 @@ private: static void saveObjectsMaterialAs(const LLGLTFMaterial *render_material, const LLLocalGLTFMaterial *local_material, const LLPermissions& permissions, const LLUUID& object_id /* = LLUUID::null */, const LLUUID& item /* = LLUUID::null */); static bool updateInventoryItem(const std::string &buffer, const LLUUID &item_id, const LLUUID &task_id); - static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions); + static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions, const LLUUID& upload_folder); void setFromGLTFMaterial(LLGLTFMaterial* mat); bool setFromSelection(); @@ -249,6 +250,7 @@ private: friend class LLMaterialFilePicker; LLUUID mAssetID; + LLUUID mUploadFolder; LLTextureCtrl* mBaseColorTextureCtrl; LLTextureCtrl* mMetallicTextureCtrl; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index eb9e054600..07d68fc3ec 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -816,8 +816,12 @@ public: }; -void log_upload_error(LLCore::HttpStatus status, const LLSD& content, - const char * const stage, const std::string & model_name) +void log_upload_error( + LLCore::HttpStatus status, + const LLSD& content, + const char * const stage, + const std::string & model_name, + const std::vector & texture_filenames) { // Add notification popup. LLSD args; @@ -875,6 +879,20 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, error_num++; } } + + if (err.has("TextureIndex")) + { + S32 texture_index = err["TextureIndex"].asInteger(); + if (texture_index < texture_filenames.size()) + { + args["MESSAGE"] = message + "\n" + texture_filenames[texture_index]; + } + else + { + llassert(false); // figure out why or how texture wasn't in the list + args["MESSAGE"] = message + llformat("\nTexture index: %d", texture_index); + } + } } else { @@ -2553,7 +2571,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, - const std::string & upload_url, bool do_upload, + const std::string & upload_url, LLUUID destination_folder_id, bool do_upload, LLHandle fee_observer, LLHandle upload_observer) : LLThread("mesh upload"), @@ -2561,6 +2579,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mDiscarded(false), mDoUpload(do_upload), mWholeModelUploadURL(upload_url), + mDestinationFolderId(destination_folder_id), mFeeObserverHandle(fee_observer), mUploadObserverHandle(upload_observer) { @@ -2677,13 +2696,21 @@ LLSD llsd_from_file(std::string filename) return result; } -void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) +void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector& texture_list_dest, bool include_textures) { LLSD result; LLSD res; + if (mDestinationFolderId.isNull()) + { result["folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_OBJECT); result["texture_folder_id"] = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE); + } + else + { + result["folder_id"] = mDestinationFolderId; + result["texture_folder_id"] = mDestinationFolderId; + } result["asset_type"] = "mesh"; result["inventory_type"] = "object"; result["description"] = "(No Description)"; @@ -2830,7 +2857,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) LLPointer upload_file = LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage()); - if (!upload_file.isNull() && upload_file->getDataSize()) + if (!upload_file.isNull() && upload_file->getDataSize() && !upload_file->isBufferInvalid()) { texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize()); } @@ -2844,6 +2871,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) texture_index[texture] = texture_num; std::string str = texture_str.str(); res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end()); + // store indexes for error handling; + texture_list_dest.push_back(material.mDiffuseMapFilename); texture_num++; } @@ -3109,7 +3138,8 @@ void LLMeshUploadThread::doWholeModelUpload() LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL; mModelData = LLSD::emptyMap(); - wholeModelToLLSD(mModelData, true); + mTextureFiles.clear(); + wholeModelToLLSD(mModelData, mTextureFiles, true); LLSD body = mModelData["asset_resources"]; dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); @@ -3162,7 +3192,8 @@ void LLMeshUploadThread::requestWholeModelFee() generateHulls(); mModelData = LLSD::emptyMap(); - wholeModelToLLSD(mModelData, false); + mTextureFiles.clear(); + wholeModelToLLSD(mModelData, mTextureFiles, false); dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, mHttpPolicyClass, @@ -3228,7 +3259,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp body["error"] = LLSD::emptyMap(); body["error"]["message"] = reason; body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py - log_upload_error(status, body, "upload", mModelData["name"].asString()); + log_upload_error(status, body, "upload", mModelData["name"].asString(), mTextureFiles); if (observer) { @@ -3263,7 +3294,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp else { LL_WARNS(LOG_MESH) << "Upload failed. Not in expected 'complete' state." << LL_ENDL; - log_upload_error(status, body, "upload", mModelData["name"].asString()); + log_upload_error(status, body, "upload", mModelData["name"].asString(), mTextureFiles); if (observer) { @@ -3288,7 +3319,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp body["error"] = LLSD::emptyMap(); body["error"]["message"] = reason; body["error"]["identifier"] = "NetworkError"; // from asset-upload/upload_util.py - log_upload_error(status, body, "fee", mModelData["name"].asString()); + log_upload_error(status, body, "fee", mModelData["name"].asString(), mTextureFiles); if (observer) { @@ -3321,7 +3352,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp else { LL_WARNS(LOG_MESH) << "Fee request failed. Not in expected 'upload' state." << LL_ENDL; - log_upload_error(status, body, "fee", mModelData["name"].asString()); + log_upload_error(status, body, "fee", mModelData["name"].asString(), mTextureFiles); if (observer) { @@ -4997,12 +5028,12 @@ bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, - std::string upload_url, bool do_upload, + std::string upload_url, const LLUUID& destination_folder_id, bool do_upload, LLHandle fee_observer, LLHandle upload_observer) { LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, lock_scale_if_joint_position, - upload_url, do_upload, fee_observer, upload_observer); + upload_url, destination_folder_id, do_upload, fee_observer, upload_observer); mUploadWaitList.push_back(thread); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 4c3901408f..2b772f7803 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -705,10 +705,13 @@ public: LLHost mHost; std::string mWholeModelFeeCapability; std::string mWholeModelUploadURL; + LLUUID mDestinationFolderId; LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, - const std::string & upload_url, bool do_upload = true, + const std::string & upload_url, + const LLUUID destination_folder_id = LLUUID::null, + bool do_upload = true, LLHandle fee_observer = (LLHandle()), LLHandle upload_observer = (LLHandle())); ~LLMeshUploadThread(); @@ -724,7 +727,7 @@ public: void doWholeModelUpload(); void requestWholeModelFee(); - void wholeModelToLLSD(LLSD& dest, bool include_textures); + void wholeModelToLLSD(LLSD& dest, std::vector& texture_list_dest, bool include_textures); void decomposeMeshMatrix(LLMatrix4& transformation, LLVector3& result_pos, @@ -745,6 +748,7 @@ private: bool mDoUpload; // if false only model data will be requested, otherwise the model will be uploaded LLSD mModelData; + std::vector mTextureFiles; // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; @@ -867,7 +871,9 @@ public: void uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, - std::string upload_url, bool do_upload = true, + std::string upload_url, + const LLUUID& destination_folder_id = LLUUID::null, + bool do_upload = true, LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index d68fc9c02d..e0649e1d88 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2393,7 +2393,7 @@ void LLModelPreview::updateStatusMessages() if (lod != lod_high) { - if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) + if (total_submeshes[lod] && total_submeshes[lod] > total_submeshes[lod_high]) { //number of submeshes is different message = "mesh_status_submesh_mismatch"; upload_status[lod] = 2; @@ -3395,7 +3395,6 @@ bool LLModelPreview::render() fmp->setViewOptionEnabled("show_skin_weight", show_skin_weight); } } - //if (this) return TRUE; if (upload_skin && !has_skin_weights) { //can't upload skin weights if model has no skin weights diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 98b7d74cd2..3adee9fa16 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -87,9 +87,16 @@ LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p) mItemsInRow(p.items_in_row), mRowPanWidthFactor(p.row_panel_width_factor), mGalleryWidthFactor(p.gallery_width_factor), - mTextureSelected(NULL) + mTextureSelected(NULL), + mSortMenu(nullptr) { updateGalleryWidth(); + + LLControlVariable* ctrl = gSavedSettings.getControl("InventoryFavoritesColorText"); + if (ctrl) + { + mSavedSettingInvFavColor = ctrl->getSignal()->connect(boost::bind(&LLOutfitGallery::handleInvFavColorChange, this)); + } } LLOutfitGallery::Params::Params() @@ -420,19 +427,32 @@ void LLOutfitGallery::updateRowsIfNeeded() bool compareGalleryItem(LLOutfitGalleryItem* item1, LLOutfitGalleryItem* item2) { - static LLCachedControl outfit_gallery_sort_by_name(gSavedSettings, "OutfitGallerySortByName"); - if(outfit_gallery_sort_by_name || - ((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage()))) + static LLCachedControl sort_by_name(gSavedSettings, "OutfitGallerySortOrder", 0); + switch (sort_by_name()) { - std::string name1 = item1->getItemName(); - std::string name2 = item2->getItemName(); + case 2: + // Sort by favorites - favorite items first, then alphabetically + if (item1->isFavorite() != item2->isFavorite()) + { + return item1->isFavorite(); + } + break; + case 1: + // Sort by images - items with non-default images first, then alphabetically + if (item1->isDefaultImage() != item2->isDefaultImage()) + { + return item2->isDefaultImage(); + } + break; + default: + // Sort alphabetically only + break; + } - return (LLStringUtil::compareDict(name1, name2) < 0); - } - else - { - return item2->isDefaultImage(); - } + // Final comparison is always alphabetical by name + std::string name1 = item1->getItemName(); + std::string name2 = item2->getItemName(); + return (LLStringUtil::compareDict(name1, name2) < 0); } void LLOutfitGallery::reArrangeRows(S32 row_diff) @@ -475,6 +495,20 @@ void LLOutfitGallery::updateGalleryWidth() mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap; } +void LLOutfitGallery::handleInvFavColorChange() +{ + for (outfit_map_t::iterator iter = mOutfitMap.begin(); + iter != mOutfitMap.end(); + ++iter) + { + if (!iter->second) continue; + LLOutfitGalleryItem* item = (LLOutfitGalleryItem*)iter->second; + + // refresh font color + item->setOutfitFavorite(item->isFavorite()); + } +} + LLPanel* LLOutfitGallery::addLastRow() { mRowCount++; @@ -626,7 +660,7 @@ void LLOutfitGallery::removeFromLastRow(LLOutfitGalleryItem* item) mItemPanels.pop_back(); } -LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID outfit_id) +LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID outfit_id, bool is_favorite) { LLOutfitGalleryItem::Params giparams; LLOutfitGalleryItem* gitem = LLUICtrlFactory::create(giparams); @@ -635,6 +669,7 @@ LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID gitem->setFollowsLeft(); gitem->setFollowsTop(); gitem->setOutfitName(name); + gitem->setOutfitFavorite(is_favorite); gitem->setUUID(outfit_id); gitem->setGallery(this); return gitem; @@ -797,8 +832,7 @@ void LLOutfitGallery::updateAddedCategory(LLUUID cat_id) return; } - std::string name = cat->getName(); - LLOutfitGalleryItem* item = buildGalleryItem(name, cat_id); + LLOutfitGalleryItem* item = buildGalleryItem(cat->getName(), cat_id, cat->getIsFavorite()); mOutfitMap.insert(LLOutfitGallery::outfit_map_value_t(cat_id, item)); item->setRightMouseDownCallback(boost::bind(&LLOutfitListBase::outfitRightClickCallBack, this, _1, _2, _3, cat_id)); @@ -861,6 +895,7 @@ void LLOutfitGallery::updateChangedCategoryName(LLViewerInventoryCategory *cat, if (item) { item->setOutfitName(name); + item->setOutfitFavorite(cat->getIsFavorite()); } } } @@ -941,6 +976,10 @@ LLOutfitListGearMenuBase* LLOutfitGallery::createGearMenu() static LLDefaultChildRegistry::Register r("outfit_gallery_item"); +bool LLOutfitGalleryItem::sColorSetInitialized = false; +LLUIColor LLOutfitGalleryItem::sDefaultTextColor; +LLUIColor LLOutfitGalleryItem::sDefaultFavoriteColor; + LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) : LLPanel(p), mGallery(nullptr), @@ -952,6 +991,12 @@ LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) mUUID(LLUUID()) { buildFromFile("panel_outfit_gallery_item.xml"); + if (!sColorSetInitialized) + { + sDefaultTextColor = LLUIColorTable::instance().getColor("White", LLColor4::white); + sDefaultFavoriteColor = LLUIColorTable::instance().getColor("InventoryFavoriteColor", LLColor4::white); + sColorSetInitialized = true; + } } LLOutfitGalleryItem::~LLOutfitGalleryItem() @@ -998,6 +1043,19 @@ void LLOutfitGalleryItem::draw() gl_draw_scaled_image(interior.mLeft - 1, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); } + static LLUICachedControl draw_star("InventoryFavoritesUseStar", true); + if(mFavorite && draw_star()) + { + const S32 HPAD = 3; + const S32 VPAD = 6; // includes padding for text and for the image + const S32 image_size = 14; + static LLPointer fav_img = LLRender2D::getInstance()->getUIImage("Inv_Favorite_Star_Full"); + + const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + gl_draw_scaled_image( + border.getWidth() - image_size - HPAD, image_size + VPAD + mOutfitNameText->getRect().getHeight(), + image_size, image_size, fav_img->getImage(), UI_VERTEX_COLOR % alpha); + } } void LLOutfitGalleryItem::setOutfitName(std::string name) @@ -1007,18 +1065,28 @@ void LLOutfitGalleryItem::setOutfitName(std::string name) mOutfitName = name; } +void LLOutfitGalleryItem::setOutfitFavorite(bool is_favorite) +{ + mFavorite = is_favorite; + + static LLCachedControl use_color(gSavedSettings, "InventoryFavoritesColorText"); + mOutfitNameText->setReadOnlyColor((mFavorite && use_color()) ? sDefaultFavoriteColor : sDefaultTextColor); +} + void LLOutfitGalleryItem::setOutfitWorn(bool value) { mWorn = value; LLStringUtil::format_map_t worn_string_args; std::string worn_string = getString("worn_string", worn_string_args); - LLUIColor text_color = LLUIColorTable::instance().getColor("White", LLColor4::white); - mOutfitWornText->setReadOnlyColor(text_color); - mOutfitNameText->setReadOnlyColor(text_color); + mOutfitWornText->setReadOnlyColor(sDefaultTextColor.get()); + mOutfitNameText->setReadOnlyColor(sDefaultTextColor.get()); mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall()); mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall()); mOutfitWornText->setValue(value ? worn_string : ""); mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes + + static LLCachedControl use_color(gSavedSettings, "InventoryFavoritesColorText"); + mOutfitNameText->setReadOnlyColor((mFavorite && use_color()) ? sDefaultFavoriteColor : sDefaultTextColor); } void LLOutfitGalleryItem::setSelected(bool value) @@ -1170,6 +1238,7 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu() registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id)); registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2)); registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id)); + registrar.add("Outfit.Favorite", boost::bind(&LLOutfitGalleryContextMenu::onFavorite, this, selected_id)); registrar.add("Outfit.Save", boost::bind(&LLOutfitGalleryContextMenu::onSave, this, selected_id)); enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2)); enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2)); @@ -1208,25 +1277,12 @@ void LLOutfitGalleryGearMenu::onUpdateItemsVisibility() { if (!mMenu) return; bool have_selection = getSelectedOutfitID().notNull(); - mMenu->setItemVisible("expand", false); - mMenu->setItemVisible("collapse", false); mMenu->setItemVisible("thumbnail", have_selection); mMenu->setItemVisible("sepatator3", true); mMenu->setItemVisible("sort_folders_by_name", true); LLOutfitListGearMenuBase::onUpdateItemsVisibility(); } -void LLOutfitGalleryGearMenu::onChangeSortOrder() -{ - bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName"); - gSavedSettings.setBOOL("OutfitGallerySortByName", sort_by_name); - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - if (gallery) - { - gallery->reArrangeRows(); - } -} - bool LLOutfitGalleryGearMenu::hasDefaultImage() { LLOutfitGallery* gallery = dynamic_cast(mOutfitList); @@ -1343,6 +1399,15 @@ void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) } } +LLToggleableMenu* LLOutfitGallery::getSortMenu() +{ + if (!mSortMenu) + { + mSortMenu = new LLOutfitGallerySortMenu(this); + } + return mSortMenu->getMenu(); +} + LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id) { outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id); @@ -1358,3 +1423,84 @@ LLUUID LLOutfitGallery::getDefaultPhoto() return LLUUID(); } + +//////////////////// LLOutfitGallerySortMenu //////////////////// + +LLOutfitGallerySortMenu::LLOutfitGallerySortMenu(LLOutfitListBase* parent_panel) + : mPanelHandle(parent_panel->getHandle()) +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Sort.OnSort", boost::bind(&LLOutfitGallerySortMenu::onSort, this, _2)); + enable_registrar.add("Sort.OnEnable", boost::bind(&LLOutfitGallerySortMenu::onEnable, this, _2)); + + mMenu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_outfit_gallery_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + llassert(mMenu); +} + + +LLToggleableMenu* LLOutfitGallerySortMenu::getMenu() +{ + return mMenu; +} + +void LLOutfitGallerySortMenu::updateItemsVisibility() +{ + onUpdateItemsVisibility(); +} + +void LLOutfitGallerySortMenu::onUpdateItemsVisibility() +{ + if (!mMenu) return; +} + +bool LLOutfitGallerySortMenu::onEnable(LLSD::String param) +{ + static LLCachedControl sort_order(gSavedSettings, "OutfitGallerySortOrder", 0); + if ("favorites_to_top" == param) + { + return sort_order == 2; + } + else if ("images_to_top" == param) + { + return sort_order == 1; + } + else if ("by_name" == param) + { + return sort_order == 0; + } + + return false; +} + +void LLOutfitGallerySortMenu::onSort(LLSD::String param) +{ + S32 sort_order = gSavedSettings.getS32("OutfitGallerySortOrder"); + S32 new_sort_order = 0; + if ("favorites_to_top" == param) + { + new_sort_order = 2; + } + else if ("images_to_top" == param) + { + new_sort_order = 1; + } + else if ("by_name" == param) + { + new_sort_order = 0; + } + if (sort_order == new_sort_order) + { + new_sort_order = sort_order ? 0 : 1; + } + gSavedSettings.setS32("OutfitGallerySortOrder", new_sort_order); + + LLOutfitGallery* gallery = dynamic_cast(mPanelHandle.get()); + if (gallery) + { + gallery->reArrangeRows(); + } +} + diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index a2a5fe26eb..5801a32a39 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -42,11 +42,13 @@ class LLOutfitGalleryItem; class LLOutfitListGearMenuBase; class LLOutfitGalleryGearMenu; class LLOutfitGalleryContextMenu; +class LLOutfitGallerySortMenu; class LLOutfitGallery : public LLOutfitListBase { public: friend class LLOutfitGalleryGearMenu; + friend class LLOutfitGallerySortMenu; friend class LLOutfitGalleryContextMenu; friend class LLUpdateGalleryOnPhotoLinked; @@ -102,10 +104,12 @@ public: /*virtual*/ bool getHasExpandableFolders() { return false; } + /*virtual*/ void onChangeSortOrder(const LLSD& userdata) {}; void updateMessageVisibility(); bool hasDefaultImage(const LLUUID& outfit_cat_id); void refreshOutfit(const LLUUID& category_id); + virtual LLToggleableMenu* getSortMenu(); protected: /*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id); @@ -133,8 +137,9 @@ private: void reArrangeRows(S32 row_diff = 0); void updateRowsIfNeeded(); void updateGalleryWidth(); + void handleInvFavColorChange(); - LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id); + LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id, bool is_favorite); LLOutfitGalleryItem* getSelectedItem() const; LLOutfitGalleryItem* getItem(const LLUUID& id) const; @@ -176,6 +181,7 @@ private: int mGalleryWidthFactor; LLListContextMenu* mOutfitGalleryMenu; + LLOutfitGallerySortMenu* mSortMenu; typedef std::map outfit_map_t; typedef outfit_map_t::value_type outfit_map_value_t; @@ -184,6 +190,11 @@ private: typedef item_num_map_t::value_type item_numb_map_value_t; item_num_map_t mItemIndexMap; std::map mIndexToItemMap; + + + LLInventoryCategoriesObserver* mOutfitsObserver; + + boost::signals2::connection mSavedSettingInvFavColor; }; class LLOutfitGalleryContextMenu : public LLOutfitContextMenu { @@ -210,8 +221,6 @@ public: protected: /*virtual*/ void onUpdateItemsVisibility(); private: - /*virtual*/ void onChangeSortOrder(); - bool hasDefaultImage(); }; @@ -240,6 +249,7 @@ public: bool setImageAssetId(LLUUID asset_id); LLUUID getImageAssetId(); void setOutfitName(std::string name); + void setOutfitFavorite(bool is_favorite); void setOutfitWorn(bool value); void setSelected(bool value); void setUUID(const LLUUID &outfit_id) {mUUID = outfit_id;} @@ -247,6 +257,7 @@ public: std::string getItemName() {return mOutfitName;} bool isDefaultImage() {return mDefaultImage;} + bool isFavorite() { return mFavorite; } bool isHidden() {return mHidden;} void setHidden(bool hidden) {mHidden = hidden;} @@ -264,7 +275,29 @@ private: bool mWorn; bool mDefaultImage; bool mHidden; + bool mFavorite; std::string mOutfitName; + + static bool sColorSetInitialized; + static LLUIColor sDefaultTextColor; + static LLUIColor sDefaultFavoriteColor; +}; + +class LLOutfitGallerySortMenu +{ +public: + LLOutfitGallerySortMenu(LLOutfitListBase* parent_panel); + + LLToggleableMenu* getMenu(); + void updateItemsVisibility(); + +private: + void onUpdateItemsVisibility(); + bool onEnable(LLSD::String param); + void onSort(LLSD::String param); + + LLToggleableMenu* mMenu; + LLHandle mPanelHandle; }; #endif // LL_LLOUTFITGALLERYCTRL_H diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 36ffcae8df..cb2a6191fa 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -36,6 +36,7 @@ #include "llagentwearables.h" #include "llaisapi.h" #include "llappearancemgr.h" +#include "llappviewer.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llinspecttexture.h" @@ -47,6 +48,7 @@ #include "lloutfitobserver.h" #include "lltoggleablemenu.h" #include "lltransutil.h" +#include "llviewercontrol.h" #include "llviewermenu.h" #include "llvoavatar.h" #include "llvoavatarself.h" @@ -55,14 +57,24 @@ static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y); static const LLOutfitTabNameComparator OUTFIT_TAB_NAME_COMPARATOR; +static const LLOutfitTabFavComparator OUTFIT_TAB_FAV_COMPARATOR; /*virtual*/ bool LLOutfitTabNameComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const { - std::string name1 = tab1->getTitle(); - std::string name2 = tab2->getTitle(); + return (LLStringUtil::compareDict(tab1->getTitle(), tab2->getTitle()) < 0); +} - return (LLStringUtil::compareDict(name1, name2) < 0); +bool LLOutfitTabFavComparator::compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const +{ + LLOutfitAccordionCtrlTab* taba = (LLOutfitAccordionCtrlTab*)tab1; + LLOutfitAccordionCtrlTab* tabb = (LLOutfitAccordionCtrlTab*)tab2; + if (taba->getFavorite() != tabb->getFavorite()) + { + return taba->getFavorite(); + } + + return (LLStringUtil::compareDict(tab1->getTitle(), tab2->getTitle()) < 0); } struct outfit_accordion_tab_params : public LLInitParam::Block @@ -82,6 +94,9 @@ const outfit_accordion_tab_params& get_accordion_tab_params() { initialized = true; + LLOutfitAccordionCtrlTab::sFavoriteIcon = LLUI::getUIImage("Inv_Favorite_Star_Full"); + LLOutfitAccordionCtrlTab::sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", LLColor4U(255, 255, 255)); + LLXMLNodePtr xmlNode; if (LLUICtrlFactory::getLayeredXMLNode("outfit_accordion_tab.xml", xmlNode)) { @@ -105,11 +120,20 @@ LLOutfitsList::LLOutfitsList() , mAccordion(NULL) , mListCommands(NULL) , mItemSelected(false) + , mSortMenu(nullptr) { + LLControlVariable* ctrl = gSavedSettings.getControl("InventoryFavoritesColorText"); + if (ctrl) + { + mSavedSettingInvFavColor = ctrl->getSignal()->connect(boost::bind(&LLOutfitsList::handleInvFavColorChange, this)); + } } LLOutfitsList::~LLOutfitsList() { + delete mSortMenu; + mSavedSettingInvFavColor.disconnect(); + mGearMenuConnection.disconnect(); } bool LLOutfitsList::postBuild() @@ -117,9 +141,25 @@ bool LLOutfitsList::postBuild() mAccordion = getChild("outfits_accordion"); mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); + initComparator(); + return LLOutfitListBase::postBuild(); } +void LLOutfitsList::initComparator() +{ + S32 mode = gSavedSettings.getS32("OutfitListSortOrder"); + if (mode == 0) + { + mAccordion->setComparator(&OUTFIT_TAB_NAME_COMPARATOR); + } + else + { + mAccordion->setComparator(&OUTFIT_TAB_FAV_COMPARATOR); + } + sortOutfits(); +} + //virtual void LLOutfitsList::onOpen(const LLSD& info) { @@ -167,6 +207,7 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id) tab->setName(name); tab->setTitle(name); + tab->setFavorite(cat->getIsFavorite()); // *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated. tab->setDisplayChildren(false); @@ -196,8 +237,9 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id) // Setting callback to reset items selection inside outfit on accordion collapsing and expanding (EXT-7875) tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::resetItemSelection, this, list, cat_id)); - // force showing list items that don't match current filter(EXT-7158) - list->setForceShowingUnmatchedItems(true); + // Depending on settings, force showing list items that don't match current filter(EXT-7158) + static LLCachedControl list_filter(gSavedSettings, "OutfitListFilterFullList"); + list->setForceShowingUnmatchedItems(list_filter(), false); // Setting list commit callback to monitor currently selected wearable item. list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1)); @@ -272,13 +314,11 @@ void LLOutfitsList::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id) { if (mOutfitsMap[prev_id]) { - mOutfitsMap[prev_id]->setTitleFontStyle("NORMAL"); - mOutfitsMap[prev_id]->setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); + ((LLOutfitAccordionCtrlTab*)mOutfitsMap[prev_id])->setOutfitSelected(false); } if (mOutfitsMap[base_id]) { - mOutfitsMap[base_id]->setTitleFontStyle("BOLD"); - mOutfitsMap[base_id]->setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor")); + ((LLOutfitAccordionCtrlTab*)mOutfitsMap[base_id])->setOutfitSelected(true); } } @@ -336,6 +376,11 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid) } } +void LLOutfitListBase::onAction(const LLSD& userdata) +{ + performAction(userdata.asString()); +} + // virtual bool LLOutfitListBase::isActionEnabled(const LLSD& userdata) { @@ -448,11 +493,12 @@ void LLOutfitsList::updateChangedCategoryName(LLViewerInventoryCategory *cat, st if (outfits_iter != mOutfitsMap.end()) { // Update tab name with the new category name. - LLAccordionCtrlTab* tab = outfits_iter->second; + LLOutfitAccordionCtrlTab* tab = (LLOutfitAccordionCtrlTab*) outfits_iter->second; if (tab) { tab->setName(name); tab->setTitle(name); + tab->setFavorite(cat->getIsFavorite()); } } } @@ -544,7 +590,7 @@ void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, cons LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); if (list) { - list->setFilterSubString(new_string, tab->getDisplayChildren()); + list->setFilterSubString(new_string, true); } if (old_string.empty()) @@ -761,6 +807,75 @@ void LLOutfitsList::onOutfitRightClick(LLUICtrl* ctrl, S32 x, S32 y, const LLUUI } } +void LLOutfitsList::handleInvFavColorChange() +{ + for (outfits_map_t::iterator iter = mOutfitsMap.begin(); + iter != mOutfitsMap.end(); + ++iter) + { + if (!iter->second) continue; + LLOutfitAccordionCtrlTab* tab = (LLOutfitAccordionCtrlTab*)iter->second; + + // refresh font color + tab->setFavorite(tab->getFavorite()); + } +} + +void LLOutfitsList::onChangeSortOrder(const LLSD& userdata) +{ + std::string sort_data = userdata.asString(); + if (sort_data == "favorites_to_top") + { + // at the moment this is a toggle + S32 val = gSavedSettings.getS32("OutfitListSortOrder"); + gSavedSettings.setS32("OutfitListSortOrder", (val ? 0 : 1)); + + initComparator(); + } + else if (sort_data == "show_entire_outfit") + { + bool new_val = !gSavedSettings.getBOOL("OutfitListFilterFullList"); + gSavedSettings.setBOOL("OutfitListFilterFullList", new_val); + + if (!getFilterSubString().empty()) + { + for (outfits_map_t::value_type& outfit : mOutfitsMap) + { + LLAccordionCtrlTab* tab = outfit.second; + const LLUUID& category_id = outfit.first; + if (!tab) continue; + + LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); + if (list) + { + list->setForceRefresh(true); + list->setForceShowingUnmatchedItems(new_val, tab->getDisplayChildren()); + } + applyFilterToTab(category_id, tab, getFilterSubString()); + } + mAccordion->arrange(); + } + } +} + +LLToggleableMenu* LLOutfitsList::getSortMenu() +{ + if (!mSortMenu) + { + mSortMenu = new LLOutfitListSortMenu(this); + } + return mSortMenu->getMenu(); +} + +void LLOutfitsList::updateMenuItemsVisibility() +{ + if (mSortMenu) + { + mSortMenu->updateItemsVisibility(); + } + LLOutfitListBase::updateMenuItemsVisibility(); +} + LLOutfitListGearMenuBase* LLOutfitsList::createGearMenu() { return new LLOutfitListGearMenu(this); @@ -778,10 +893,10 @@ bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y) LLOutfitListBase::LLOutfitListBase() : LLPanelAppearanceTab() , mIsInitialized(false) + , mGearMenu(nullptr) { mCategoriesObserver = new LLInventoryCategoriesObserver(); mOutfitMenu = new LLOutfitContextMenu(this); - //mGearMenu = createGearMenu(); } LLOutfitListBase::~LLOutfitListBase() @@ -877,6 +992,10 @@ bool LLOutfitListBase::isOutfitFolder(LLViewerInventoryCategory* cat) const void LLOutfitListBase::refreshList(const LLUUID& category_id) { + if (LLAppViewer::instance()->quitRequested()) + { + return; + } bool wasNull = mRefreshListState.CategoryUUID.isNull(); mRefreshListState.CategoryUUID.setNull(); @@ -935,8 +1054,18 @@ void LLOutfitListBase::onIdle(void* userdata) void LLOutfitListBase::onIdleRefreshList() { - if (mRefreshListState.CategoryUUID.isNull()) + if (LLAppViewer::instance()->quitRequested()) + { + mRefreshListState.CategoryUUID.setNull(); + gIdleCallbacks.deleteFunction(onIdle, this); return; + } + if (mRefreshListState.CategoryUUID.isNull()) + { + LL_WARNS() << "Called onIdleRefreshList without id" << LL_ENDL; + gIdleCallbacks.deleteFunction(onIdle, this); + return; + } const F64 MAX_TIME = 0.05f; F64 curent_time = LLTimer::getTotalSeconds(); @@ -1078,12 +1207,6 @@ void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LL bool LLOutfitListBase::postBuild() { - mGearMenu = createGearMenu(); - - LLMenuButton* menu_gear_btn = getChild("options_gear_btn"); - - menu_gear_btn->setMouseDownCallback(boost::bind(&LLOutfitListGearMenuBase::updateItemsVisibility, mGearMenu)); - menu_gear_btn->setMenu(mGearMenu->getMenu()); return true; } @@ -1097,6 +1220,20 @@ void LLOutfitListBase::expandAllFolders() onExpandAllFolders(); } +void LLOutfitListBase::updateMenuItemsVisibility() +{ + mGearMenu->updateItemsVisibility(); +} + +LLToggleableMenu* LLOutfitListBase::getGearMenu() +{ + if (!mGearMenu) + { + mGearMenu = createGearMenu(); + } + return mGearMenu->getMenu(); +}; + void LLOutfitListBase::deselectOutfit(const LLUUID& category_id) { // Reset selection if the outfit is selected. @@ -1123,6 +1260,7 @@ LLContextMenu* LLOutfitContextMenu::createMenu() registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); registrar.add("Outfit.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitContextMenu::onThumbnail, this, selected_id)); + registrar.add("Outfit.Favorite", boost::bind(&LLOutfitContextMenu::onFavorite, this, selected_id)); registrar.add("Outfit.Save", boost::bind(&LLOutfitContextMenu::onSave, this, selected_id)); enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitContextMenu::onEnable, this, _2)); @@ -1173,6 +1311,16 @@ bool LLOutfitContextMenu::onVisible(LLSD::String param) { return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id); } + else if ("favorites_add" == param) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_cat_id); + return cat && !cat->getIsFavorite(); + } + else if ("favorites_remove" == param) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_cat_id); + return cat && cat->getIsFavorite(); + } return true; } @@ -1197,6 +1345,14 @@ void LLOutfitContextMenu::onThumbnail(const LLUUID &outfit_cat_id) } } +void LLOutfitContextMenu::onFavorite(const LLUUID& outfit_cat_id) +{ + if (outfit_cat_id.notNull()) + { + toggle_favorite(outfit_cat_id); + } +} + void LLOutfitContextMenu::onSave(const LLUUID &outfit_cat_id) { if (outfit_cat_id.notNull()) @@ -1227,14 +1383,13 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist) registrar.add("Gear.Rename", boost::bind(&LLOutfitListGearMenuBase::onRename, this)); registrar.add("Gear.Delete", boost::bind(&LLOutfitListBase::removeSelected, mOutfitList)); registrar.add("Gear.Create", boost::bind(&LLOutfitListGearMenuBase::onCreate, this, _2)); - registrar.add("Gear.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, mOutfitList)); - registrar.add("Gear.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, mOutfitList)); registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); registrar.add("Gear.Save", boost::bind(&LLOutfitListGearMenuBase::onSave, this)); registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this)); - registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this)); + registrar.add("Gear.Favorite", boost::bind(&LLOutfitListGearMenuBase::onFavorite, this)); + registrar.add("Gear.SortByImage", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this)); enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); enable_registrar.add("Gear.OnVisible", boost::bind(&LLOutfitListGearMenuBase::onVisible, this, _2)); @@ -1368,6 +1523,16 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) { return false; } + else if ("favorites_add" == param) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); + return cat && !cat->getIsFavorite(); + } + else if ("favorites_remove" == param) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_outfit_id); + return cat && cat->getIsFavorite(); + } return true; } @@ -1379,6 +1544,12 @@ void LLOutfitListGearMenuBase::onThumbnail() LLFloaterReg::showInstance("change_item_thumbnail", data); } +void LLOutfitListGearMenuBase::onFavorite() +{ + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + toggle_favorite(selected_outfit_id); +} + void LLOutfitListGearMenuBase::onChangeSortOrder() { @@ -1394,14 +1565,79 @@ LLOutfitListGearMenu::~LLOutfitListGearMenu() void LLOutfitListGearMenu::onUpdateItemsVisibility() { if (!mMenu) return; - mMenu->setItemVisible("expand", true); - mMenu->setItemVisible("collapse", true); mMenu->setItemVisible("thumbnail", getSelectedOutfitID().notNull()); + mMenu->setItemVisible("favorite", getSelectedOutfitID().notNull()); mMenu->setItemVisible("sepatator3", false); mMenu->setItemVisible("sort_folders_by_name", false); LLOutfitListGearMenuBase::onUpdateItemsVisibility(); } +//////////////////// LLOutfitListSortMenu //////////////////// + +LLOutfitListSortMenu::LLOutfitListSortMenu(LLOutfitListBase* parent_panel) + : mPanelHandle(parent_panel->getHandle()) +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Sort.Collapse", boost::bind(&LLOutfitListBase::onCollapseAllFolders, parent_panel)); + registrar.add("Sort.Expand", boost::bind(&LLOutfitListBase::onExpandAllFolders, parent_panel)); + registrar.add("Sort.OnSort", boost::bind(&LLOutfitListBase::onChangeSortOrder, parent_panel, _2)); + enable_registrar.add("Sort.OnEnable", boost::bind(&LLOutfitListSortMenu::onEnable, this, _2)); + + mMenu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_outfit_list_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + llassert(mMenu); +} + + +LLToggleableMenu* LLOutfitListSortMenu::getMenu() +{ + return mMenu; +} + +void LLOutfitListSortMenu::updateItemsVisibility() +{ + onUpdateItemsVisibility(); +} + +void LLOutfitListSortMenu::onUpdateItemsVisibility() +{ + if (!mMenu) return; + mMenu->setItemVisible("expand", true); + mMenu->setItemVisible("collapse", true); + mMenu->setItemVisible("sort_favorites_to_top", true); + mMenu->setItemVisible("show_entire_outfit_in_search", true); +} + +bool LLOutfitListSortMenu::onEnable(LLSD::String param) +{ + if ("favorites_to_top" == param) + { + static LLCachedControl sort_order(gSavedSettings, "OutfitListSortOrder", 0); + return sort_order == 1; + } + else if ("show_entire_outfit" == param) + { + static LLCachedControl filter_mode(gSavedSettings, "OutfitListFilterFullList", 0); + return filter_mode; + } + + return false; +} + + +//////////////////// LLOutfitAccordionCtrlTab //////////////////// + +LLUIImage* LLOutfitAccordionCtrlTab::sFavoriteIcon; +LLUIColor LLOutfitAccordionCtrlTab::sFgColor; + +void LLOutfitAccordionCtrlTab::draw() +{ + LLAccordionCtrlTab::draw(); + drawFavoriteIcon(); +} + bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) { if (y >= getLocalRect().getHeight() - getHeaderHeight()) @@ -1427,4 +1663,54 @@ bool LLOutfitAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) return LLAccordionCtrlTab::handleToolTip(x, y, mask); } + +void LLOutfitAccordionCtrlTab::setFavorite(bool is_favorite) +{ + mIsFavorite = is_favorite; + updateTitleColor(); +} + +void LLOutfitAccordionCtrlTab::setOutfitSelected(bool val) +{ + mIsSelected = val; + setTitleFontStyle(mIsSelected ? "BOLD" : "NORMAL"); + updateTitleColor(); + } + +void LLOutfitAccordionCtrlTab::updateTitleColor() + { + static LLUICachedControl highlight_color("InventoryFavoritesColorText", true); + if (mIsFavorite && highlight_color()) + { + setTitleColor(LLUIColorTable::instance().getColor("InventoryFavoriteColor")); + } + else if (mIsSelected) + { + setTitleColor(LLUIColorTable::instance().getColor("SelectedOutfitTextColor")); + } + else + { + setTitleColor(LLUIColorTable::instance().getColor("AccordionHeaderTextColor")); + } + } + +void LLOutfitAccordionCtrlTab::drawFavoriteIcon() +{ + if (!mIsFavorite) + { + return; + } + static LLUICachedControl draw_star("InventoryFavoritesUseStar", true); + if (!draw_star) + { + return; + } + + const S32 PAD = 2; + const S32 image_size = 18; + + gl_draw_scaled_image( + getRect().getWidth() - image_size - PAD, getRect().getHeight() - image_size - PAD, + image_size, image_size, sFavoriteIcon->getImage(), sFgColor); +} // EOF diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index fad0e638fb..0bf5becb05 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -41,6 +41,7 @@ class LLAccordionCtrlTab; class LLInventoryCategoriesObserver; class LLOutfitListGearMenuBase; +class LLOutfitListSortMenuBase; class LLWearableItemsList; class LLListContextMenu; @@ -61,6 +62,17 @@ public: /*virtual*/ bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const; }; +class LLOutfitTabFavComparator : public LLAccordionCtrl::LLTabComparator +{ + LOG_CLASS(LLOutfitTabFavComparator); + +public: + LLOutfitTabFavComparator() {}; + virtual ~LLOutfitTabFavComparator() {}; + + /*virtual*/ bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const; +}; + class LLOutfitListBase : public LLPanelAppearanceTab { public: @@ -92,6 +104,7 @@ public: boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); void outfitRightClickCallBack(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& cat_id); + void onAction(const LLSD& userdata); virtual bool isActionEnabled(const LLSD& userdata); virtual void performAction(std::string action); virtual bool hasItemSelected() = 0; @@ -109,6 +122,12 @@ public: virtual bool getHasExpandableFolders() = 0; + virtual void onChangeSortOrder(const LLSD& userdata) = 0; + + virtual void updateMenuItemsVisibility(); + virtual LLToggleableMenu* getGearMenu(); + virtual bool getTrashMenuVisible() { return true; }; + protected: void observerCallback(const LLUUID& category_id); virtual LLOutfitListGearMenuBase* createGearMenu() = 0; @@ -141,6 +160,7 @@ protected: selection_change_signal_t mSelectionChangeSignal; LLListContextMenu* mOutfitMenu; LLOutfitListGearMenuBase* mGearMenu; + boost::signals2::connection mGearMenuConnection; }; ////////////////////////////////////////////////////////////////////////// @@ -157,7 +177,6 @@ protected: /* virtual */ LLContextMenu* createMenu(); bool onEnable(LLSD::String param); - bool onVisible(LLSD::String param); static void editOutfit(); @@ -165,6 +184,7 @@ protected: static void renameOutfit(const LLUUID& outfit_cat_id); void onThumbnail(const LLUUID &outfit_cat_id); + void onFavorite(const LLUUID& outfit_cat_id); void onSave(const LLUUID &outfit_cat_id); private: @@ -184,6 +204,7 @@ public: protected: virtual void onUpdateItemsVisibility(); virtual void onThumbnail(); + virtual void onFavorite(); virtual void onChangeSortOrder(); const LLUUID& getSelectedOutfitID(); @@ -204,6 +225,23 @@ private: bool onVisible(LLSD::String param); }; +class LLOutfitListSortMenu +{ +public: + LLOutfitListSortMenu(LLOutfitListBase* parent_panel); + + LLToggleableMenu* getMenu(); + void updateItemsVisibility(); + +private: + void onUpdateItemsVisibility(); + bool onEnable(LLSD::String param); + + LLToggleableMenu* mMenu; + LLHandle mPanelHandle; +}; + + class LLOutfitListGearMenu : public LLOutfitListGearMenuBase { public: @@ -223,8 +261,16 @@ public: Params() : cat_id("cat_id") {} }; + virtual void draw(); virtual bool handleToolTip(S32 x, S32 y, MASK mask); + void setFavorite(bool is_favorite); + bool getFavorite() const { return mIsFavorite; } + void setOutfitSelected(bool val); + + static LLUIImage* sFavoriteIcon; + static LLUIColor sFgColor; + protected: LLOutfitAccordionCtrlTab(const LLOutfitAccordionCtrlTab::Params &p) : LLAccordionCtrlTab(p), @@ -232,7 +278,12 @@ public: {} friend class LLUICtrlFactory; + void updateTitleColor(); + void drawFavoriteIcon(); + LLUUID mFolderID; + bool mIsFavorite = false; + bool mIsSelected = false; }; /** * @class LLOutfitsList @@ -251,6 +302,7 @@ public: virtual ~LLOutfitsList(); /*virtual*/ bool postBuild(); + void initComparator(); /*virtual*/ void onOpen(const LLSD& info); @@ -289,6 +341,10 @@ public: /*virtual*/ bool getHasExpandableFolders() { return true; } + /*virtual*/ void onChangeSortOrder(const LLSD& userdata); + virtual LLToggleableMenu* getSortMenu(); + void updateMenuItemsVisibility(); + protected: LLOutfitListGearMenuBase* createGearMenu(); @@ -359,6 +415,8 @@ private: static void onOutfitRename(const LLSD& notification, const LLSD& response); + void handleInvFavColorChange(); + //LLInventoryCategoriesObserver* mCategoriesObserver; LLAccordionCtrl* mAccordion; @@ -376,13 +434,15 @@ private: // Used to monitor COF changes for updating items worn state. See EXT-8636. uuid_vec_t mCOFLinkedItems; - //LLOutfitListGearMenu* mGearMenu; + LLOutfitListSortMenu* mSortMenu; //bool mIsInitialized; /** * True if there is a selection inside currently selected outfit */ bool mItemSelected; + + boost::signals2::connection mSavedSettingInvFavColor; }; #endif //LL_LLOUTFITSLIST_H diff --git a/indra/newview/llpanelappearancetab.h b/indra/newview/llpanelappearancetab.h index e4d16582de..e088c3e6f0 100644 --- a/indra/newview/llpanelappearancetab.h +++ b/indra/newview/llpanelappearancetab.h @@ -29,6 +29,8 @@ #include "llpanel.h" +class LLToggleableMenu; + class LLPanelAppearanceTab : public LLPanel { public: @@ -47,6 +49,11 @@ public: const std::string& getFilterSubString() { return mFilterSubString; } + virtual void updateMenuItemsVisibility() = 0; + virtual LLToggleableMenu* getGearMenu() = 0; + virtual LLToggleableMenu* getSortMenu() = 0; + virtual bool getTrashMenuVisible() = 0; + protected: /** diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 9076576f00..3ab48f69c8 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -543,10 +543,6 @@ LLPanelFace::~LLPanelFace() void LLPanelFace::onVisibilityChange(bool new_visibility) { - if (new_visibility) - { - gAgent.showLatestFeatureNotification("gltf"); - } LLPanel::onVisibilityChange(new_visibility); } diff --git a/indra/newview/llpanelgroupcreate.cpp b/indra/newview/llpanelgroupcreate.cpp index 4a370525ff..bc7b5caddf 100644 --- a/indra/newview/llpanelgroupcreate.cpp +++ b/indra/newview/llpanelgroupcreate.cpp @@ -85,6 +85,7 @@ bool LLPanelGroupCreate::postBuild() mInsignia = getChild("insignia", true); mInsignia->setAllowLocalTexture(false); + mInsignia->setBakeTextureEnabled(false); mInsignia->setCanApplyImmediately(false); return true; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index ca429ae2f8..270ca29403 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -197,6 +197,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) { mInsignia->setCommitCallback(onCommitAny, this); mInsignia->setAllowLocalTexture(false); + mInsignia->setBakeTextureEnabled(false); } mFounderName = getChild("founder_name"); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 44465cbc33..ad7aa57842 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -68,6 +68,7 @@ const std::string FILTERS_FILENAME("filters.xml"); const std::string ALL_ITEMS("All Items"); const std::string RECENT_ITEMS("Recent Items"); const std::string WORN_ITEMS("Worn Items"); +const std::string FAVORITES("Favorites"); static LLPanelInjector t_inventory("panel_main_inventory"); @@ -214,6 +215,17 @@ bool LLPanelMainInventory::postBuild() worn_filter.markDefault(); mWornItemsPanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mWornItemsPanel, _1, _2)); } + + LLInventoryPanel* favorites_panel = getChild(FAVORITES); + if (favorites_panel) + { + favorites_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); + LLInventoryFilter& favorites_filter = favorites_panel->getFilter(); + favorites_filter.setEmptyLookupMessage("InventoryNoMatchingFavorites"); + favorites_filter.markDefault(); + favorites_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, favorites_panel, _1, _2)); + } + mSearchTypeCombo = getChild("search_type"); if(mSearchTypeCombo) { @@ -578,7 +590,8 @@ void LLPanelMainInventory::doCreate(const LLSD& userdata) } else { - menu_create_inventory_item(getPanel(), NULL, userdata); + selectAllItemsPanel(); + menu_create_inventory_item(mAllItemsPanel, NULL, userdata); } } diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 0a3a2e753a..23e6a9fbcf 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -2267,19 +2267,21 @@ void LLPanelObject::onCopyParams() if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) { LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); - - LLUUID texture_id = sculpt_params->getSculptTexture(); - if (get_can_copy_texture(texture_id)) + if (sculpt_params) { - LL_DEBUGS("FloaterTools") << "Recording texture" << LL_ENDL; - mClipboardParams["sculpt"]["id"] = texture_id; - } - else - { - mClipboardParams["sculpt"]["id"] = SCULPT_DEFAULT_TEXTURE; - } + LLUUID texture_id = sculpt_params->getSculptTexture(); + if (get_can_copy_texture(texture_id)) + { + LL_DEBUGS("FloaterTools") << "Recording texture" << LL_ENDL; + mClipboardParams["sculpt"]["id"] = texture_id; + } + else + { + mClipboardParams["sculpt"]["id"] = SCULPT_DEFAULT_TEXTURE; + } - mClipboardParams["sculpt"]["type"] = sculpt_params->getSculptType(); + mClipboardParams["sculpt"]["type"] = sculpt_params->getSculptType(); + } } } diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index ef7986603b..f90d6d5b3f 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -129,6 +129,7 @@ public: virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {} virtual bool isItemRenameable() const; virtual bool renameItem(const std::string& new_name); + virtual bool isFavorite() const { return false; } virtual bool isItemMovable() const; virtual bool isItemRemovable(bool check_worn = true) const; virtual bool removeItem(); @@ -1364,7 +1365,23 @@ bool LLPanelObjectInventory::postBuild() void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { - LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); + std::string action = userdata.asString(); + if ("rename" == action || "delete" == action) + { + LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); + if (objectp && !objectp->permModify()) + { + LLNotificationsUtil::add("CantModifyContentInNoModTask"); + } + else + { + LLInventoryAction::doToSelected(&gInventory, mFolders, action); + } + } + else + { + LLInventoryAction::doToSelected(&gInventory, mFolders, action); + } } void LLPanelObjectInventory::clearContents() diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 4cd4afaa5a..a9e860d2ef 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -404,7 +404,9 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() mWearableListManager(NULL), mPlusBtn(NULL), mWearablesGearMenuBtn(NULL), - mGearMenuBtn(NULL) + mGearMenuBtn(NULL), + mStatus(NULL), + mCurrentOutfitName(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(false); diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 47c02793a3..e2e2cf1a61 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -33,6 +33,7 @@ #include "llfloatersidepanelcontainer.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" +#include "llmenubutton.h" #include "llnotificationsutil.h" #include "lloutfitgallery.h" #include "lloutfitobserver.h" @@ -53,12 +54,17 @@ static const std::string SAVE_BTN("save_btn"); static LLPanelInjector t_inventory("panel_outfits_inventory"); -LLPanelOutfitsInventory::LLPanelOutfitsInventory() : - mMyOutfitsPanel(NULL), - mCurrentOutfitPanel(NULL), - mActivePanel(NULL), - mAppearanceTabs(NULL), - mInitialized(false) +LLPanelOutfitsInventory::LLPanelOutfitsInventory() + : mMyOutfitsPanel(nullptr) + , mCurrentOutfitPanel(nullptr) + , mActivePanel(nullptr) + , mAppearanceTabs(nullptr) + , mInitialized(false) + , mGearMenu(nullptr) + , mSortMenu(nullptr) + , mTrashBtn(nullptr) + , mSortMenuPanel(nullptr) + , mTrashMenuPanel(nullptr) { gAgentWearables.addLoadedCallback(boost::bind(&LLPanelOutfitsInventory::onWearablesLoaded, this)); gAgentWearables.addLoadingStartedCallback(boost::bind(&LLPanelOutfitsInventory::onWearablesLoading, this)); @@ -75,6 +81,9 @@ LLPanelOutfitsInventory::~LLPanelOutfitsInventory() { gSavedSettings.setS32("LastAppearanceTab", mAppearanceTabs->getCurrentPanelIndex()); } + mGearMenuConnection.disconnect(); + mSortMenuConnection.disconnect(); + mTrashMenuConnection.disconnect(); } // virtual @@ -258,6 +267,22 @@ void LLPanelOutfitsInventory::initListCommandsHandlers() mOutfitGalleryPanel->childSetAction("trash_btn", boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); } +void LLPanelOutfitsInventory::setMenuButtons(LLMenuButton* gear_menu, LLMenuButton* sort_menu, LLButton* trash_btn, LLPanel* sort_menu_panel, LLPanel* trash_menu_panel) +{ + mGearMenu = gear_menu; + mSortMenu = sort_menu; + mTrashBtn = trash_btn; + mSortMenuPanel = sort_menu_panel; + mTrashMenuPanel = trash_menu_panel; + + mGearMenuConnection.disconnect(); + mSortMenuConnection.disconnect(); + mTrashMenuConnection.disconnect(); + mGearMenuConnection = mGearMenu->setMouseDownCallback(boost::bind(&LLPanelOutfitsInventory::onGearMouseDown, this)); + mSortMenuConnection = mSortMenu->setMouseDownCallback(boost::bind(&LLPanelOutfitsInventory::onGearMouseDown, this)); + mTrashMenuConnection = mTrashBtn->setClickedCallback(boost::bind(&LLPanelOutfitsInventory::onTrashButtonClick, this)); +} + void LLPanelOutfitsInventory::updateListCommands() { bool trash_enabled = isActionEnabled("delete"); @@ -284,6 +309,14 @@ void LLPanelOutfitsInventory::onTrashButtonClick() } } +void LLPanelOutfitsInventory::onGearMouseDown() +{ + if (mActivePanel) + { + mActivePanel->updateMenuItemsVisibility(); + } +} + bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) { return mActivePanel && mActivePanel->isActionEnabled(userdata); @@ -320,6 +353,28 @@ void LLPanelOutfitsInventory::onTabChange() mActivePanel->checkFilterSubString(); mActivePanel->onOpen(LLSD()); + if (mGearMenu) + { + mGearMenu->setMenu(mActivePanel->getGearMenu(), LLMenuButton::MP_BOTTOM_LEFT); + } + if (mSortMenu && mSortMenuPanel) + { + LLToggleableMenu* menu = mActivePanel->getSortMenu(); + if (menu) + { + mSortMenu->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT); + mSortMenuPanel->setVisible(true); + } + else + { + mSortMenuPanel->setVisible(false); + } + } + if (mTrashMenuPanel) + { + mTrashMenuPanel->setVisible(mActivePanel->getTrashMenuVisible()); + } + updateVerbs(); } diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index e046681e95..29f7eb44f7 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -30,11 +30,13 @@ #include "llpanel.h" +class LLButton; class LLOutfitGallery; class LLOutfitsList; class LLOutfitListGearMenuBase; class LLPanelAppearanceTab; class LLPanelWearing; +class LLMenuButton; class LLMenuGL; class LLSidepanelAppearance; class LLTabContainer; @@ -63,6 +65,13 @@ public: bool isCOFPanelActive() const; + void setMenuButtons( + LLMenuButton* gear_menu, + LLMenuButton* sort_menu, + LLButton* trash_btn, + LLPanel* sort_menu_panel, + LLPanel* trash_menu_panel); + protected: void updateVerbs(); @@ -92,20 +101,29 @@ protected: void initListCommandsHandlers(); void updateListCommands(); void onWearButtonClick(); - void showGearMenu(); void onTrashButtonClick(); + void onGearMouseDown(); bool isActionEnabled(const LLSD& userdata); void setWearablesLoading(bool val); void onWearablesLoaded(); void onWearablesLoading(); private: LLPanel* mListCommands; - LLMenuGL* mMenuAdd; LLButton* mWearBtn = nullptr; // List Commands // ////////////////////////////////////////////////////////////////////////////////// bool mInitialized; + + // not owned items + LLMenuButton* mGearMenu; + LLMenuButton* mSortMenu; + LLButton* mTrashBtn; + LLPanel* mSortMenuPanel; + LLPanel* mTrashMenuPanel; + boost::signals2::connection mGearMenuConnection; + boost::signals2::connection mSortMenuConnection; + boost::signals2::connection mTrashMenuConnection; }; #endif //LL_LLPANELOUTFITSINVENTORY_H diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 939b1f12a9..62e726d21d 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -354,7 +354,10 @@ void PeopleContextMenu::eject() avatar = (LLVOAvatar*) object; } } - if (!avatar) return; + + if (!avatar) + return; + LLSD payload; payload["avatar_id"] = avatar->getID(); std::string fullname = avatar->getFullname(); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 0ce9ef7a7a..cbf5819fda 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -176,7 +176,7 @@ bool LLPanelPermissions::postBuild() childSetCommitCallback("sale type",LLPanelPermissions::onCommitSaleType,this); - childSetCommitCallback("Edit Cost", LLPanelPermissions::onCommitSaleInfo, this); + childSetCommitCallback("Edit Cost", LLPanelPermissions::onCommitSalePrice, this); childSetCommitCallback("checkbox next owner can modify",LLPanelPermissions::onCommitNextOwnerModify,this); childSetCommitCallback("checkbox next owner can copy",LLPanelPermissions::onCommitNextOwnerCopy,this); @@ -781,7 +781,9 @@ void LLPanelPermissions::refresh() if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER)) { - getChildView("checkbox for sale")->setEnabled(can_transfer || (!can_transfer && num_for_sale)); + bool change_sale_allowed = can_transfer || (!can_transfer && num_for_sale); + getChildView("checkbox for sale")->setEnabled(change_sale_allowed); + getChildView("Edit Cost")->setEnabled(change_sale_allowed && !is_for_sale_mixed); // Set the checkbox to tentative if the prices of each object selected // are not the same. getChild("checkbox for sale")->setTentative( is_for_sale_mixed); @@ -1224,6 +1226,16 @@ void LLPanelPermissions::onCommitSaleType(LLUICtrl*, void* data) self->setAllSaleInfo(); } +void LLPanelPermissions::onCommitSalePrice(LLUICtrl *, void *data) +{ + LLPanelPermissions *self = (LLPanelPermissions *) data; + LLCheckBoxCtrl *checkPurchase = self->getChild("checkbox for sale"); + if (checkPurchase && checkPurchase->get()) + { + self->setAllSaleInfo(); + } +} + void LLPanelPermissions::setAllSaleInfo() { LL_INFOS() << "LLPanelPermissions::setAllSaleInfo()" << LL_ENDL; diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h index 77129434ed..12e88361c9 100644 --- a/indra/newview/llpanelpermissions.h +++ b/indra/newview/llpanelpermissions.h @@ -77,6 +77,7 @@ protected: static void onCommitSaleInfo(LLUICtrl* ctrl, void* data); static void onCommitSaleType(LLUICtrl* ctrl, void* data); + static void onCommitSalePrice(LLUICtrl *ctrl, void *data); void setAllSaleInfo(); static void onCommitClickAction(LLUICtrl* ctrl, void*); diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index c81746a48a..b77ee2f439 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -2066,6 +2066,7 @@ void LLPanelProfileFirstLife::onChangePhoto() } }); texture_floaterp->setLocalTextureEnabled(false); + texture_floaterp->setBakeTextureEnabled(false); texture_floaterp->setCanApply(false, true, false); parent_floater->addDependentFloater(mFloaterTexturePickerHandle); diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index c1534c9abd..3aedde74c6 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -113,6 +113,7 @@ protected: boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs, no_op)); registrar.add("Wearing.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs, no_op)); + registrar.add("Wearing.Favorite", boost::bind(toggle_favorites, mUUIDs)); LLContextMenu* menu = createFromFile("menu_wearing_tab.xml"); updateMenuItemsVisibility(menu); @@ -125,6 +126,8 @@ protected: bool bp_selected = false; // true if body parts selected bool clothes_selected = false; bool attachments_selected = false; + bool can_favorite = false; + bool can_unfavorite = false; // See what types of wearables are selected. for (uuid_vec_t::const_iterator it = mUUIDs.begin(); it != mUUIDs.end(); ++it) @@ -137,6 +140,9 @@ protected: continue; } + LLUUID linked_id = item->getLinkedUUID(); + LLViewerInventoryItem* linked_item = gInventory.getItem(linked_id); + LLAssetType::EType type = item->getType(); if (type == LLAssetType::AT_CLOTHING) { @@ -150,6 +156,8 @@ protected: { attachments_selected = true; } + can_favorite |= !linked_item->getIsFavorite(); + can_unfavorite |= linked_item->getIsFavorite(); } // Enable/disable some menu items depending on the selection. @@ -166,6 +174,8 @@ protected: menu->setItemVisible("detach", allow_detach); menu->setItemVisible("edit_outfit_separator", show_touch | show_edit | allow_take_off || allow_detach); menu->setItemVisible("show_original", mUUIDs.size() == 1); + menu->setItemVisible("favorites_add", can_favorite); + menu->setItemVisible("favorites_remove", can_unfavorite); } }; @@ -232,6 +242,10 @@ LLPanelWearing::~LLPanelWearing() { mAttachmentsChangedConnection.disconnect(); } + if (mGearMenuConnection.connected()) + { + mGearMenuConnection.disconnect(); + } } bool LLPanelWearing::postBuild() @@ -249,10 +263,6 @@ bool LLPanelWearing::postBuild() mTempItemsList->setFgUnselectedColor(LLColor4::white); mTempItemsList->setRightMouseDownCallback(boost::bind(&LLPanelWearing::onTempAttachmentsListRightClick, this, _1, _2, _3)); - LLMenuButton* menu_gear_btn = getChild("options_gear_btn"); - - menu_gear_btn->setMenu(mGearMenu->getMenu()); - return true; } @@ -560,6 +570,16 @@ void LLPanelWearing::onRemoveAttachment() } } +LLToggleableMenu* LLPanelWearing::getGearMenu() +{ + return mGearMenu->getMenu(); +} + +LLToggleableMenu* LLPanelWearing::getSortMenu() +{ + return NULL; +} + void LLPanelWearing::onRemoveItem() { if (mWearablesTab->isExpanded()) diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index ea0787d0ef..aa80a3fc21 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -84,6 +84,11 @@ public: void onEditAttachment(); void onRemoveAttachment(); + void updateMenuItemsVisibility() {}; + LLToggleableMenu* getGearMenu(); + LLToggleableMenu* getSortMenu(); + bool getTrashMenuVisible() { return false; } + private: void onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); void onTempAttachmentsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); @@ -93,6 +98,7 @@ private: LLWearableItemsList* mCOFItemsList; LLScrollListCtrl* mTempItemsList; LLWearingGearMenu* mGearMenu; + boost::signals2::connection mGearMenuConnection; LLListContextMenu* mContextMenu; LLListContextMenu* mAttachmentsMenu; diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h index a818793550..8da18e0945 100644 --- a/indra/newview/llreflectionmap.h +++ b/indra/newview/llreflectionmap.h @@ -124,7 +124,7 @@ public: LLSpatialGroup* mGroup = nullptr; // viewer object this probe is tracking (if any) - LLPointer mViewerObject = nullptr; + LLPointer mViewerObject; // what priority should this probe have (higher is higher priority) // currently only 0 or 1 diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 8aa3693d60..01fd5ae63c 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -252,6 +252,7 @@ LLSelectMgr::LLSelectMgr() LLSelectMgr::~LLSelectMgr() { clearSelections(); + mSlectionLodModChangedConnection.disconnect(); } void LLSelectMgr::clearSelections() @@ -2248,6 +2249,7 @@ void LLSelectMgr::selectionRevertGLTFMaterials() { // Restore base material LLUUID asset_id = nodep->mSavedGLTFMaterialIds[te]; + LLUUID old_asset_id = objectp->getRenderMaterialID(te); // Update material locally objectp->setRenderMaterialID(te, asset_id, false /*wait for LLGLTFMaterialList update*/); @@ -2258,18 +2260,29 @@ void LLSelectMgr::selectionRevertGLTFMaterials() objectp->setTEGLTFMaterialOverride(te, material); } - // Enqueue update to server - if (asset_id.notNull() && material) - { - // Restore overrides and base material - LLGLTFMaterialList::queueApply(objectp, te, asset_id, material); - } - else + if (asset_id.isNull() || !material) { //blank override out LLGLTFMaterialList::queueApply(objectp, te, asset_id); } - + if (old_asset_id != asset_id) + { + // Restore overrides and base material + // Note: might not work reliably if asset is already there, might + // have a server sided problem where servers applies override + // first then resets it by adding asset, in which case need + // to create a server ticket and chain asset then override + // application. + LLGLTFMaterialList::queueApply(objectp, te, asset_id, material); + } + else + { + // Enqueue override update to server + // Note: this is suboptimal, better to send asset id as well + // but there seems to be a server problem with queueApply + // that ignores override in some cases + LLGLTFMaterialList::queueModify(objectp, te, material); + } } return true; } diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 792a37297f..11aad3b806 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -943,6 +943,7 @@ private: bool mForceSelection; std::vector mPauseRequests; + boost::signals2::connection mSlectionLodModChangedConnection; }; // *DEPRECATED: For callbacks or observers, use diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index c618483fc4..1d1b31d2a6 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -40,6 +40,7 @@ #include "llfloaterworldmap.h" #include "llfolderviewmodel.h" #include "llloadingindicator.h" +#include "llmenubutton.h" #include "lloutfitobserver.h" #include "llpaneleditwearable.h" #include "llpaneloutfitsinventory.h" @@ -145,6 +146,14 @@ bool LLSidepanelAppearance::postBuild() setWearablesLoading(gAgentWearables.isCOFChangeInProgress()); + + LLMenuButton* menu_gear_btn = getChild("options_gear_btn"); + LLMenuButton* menu_sort_btn = getChild("sorting_menu_btn"); + LLButton* menu_trash_btn = getChild("trash_btn"); + LLPanel* menu_sort_btn_panel = getChild("options_sort_btn_panel"); + LLPanel* menu_trash_btn_panel = getChild("trash_btn_panel"); + mPanelOutfitsInventory->setMenuButtons(menu_gear_btn, menu_sort_btn, menu_trash_btn, menu_sort_btn_panel, menu_trash_btn_panel); + return true; } diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index fccf745a74..3d4b4fb9c1 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -56,6 +56,8 @@ #include "llviewerregion.h" +const char* const DEFAULT_DESC = "(No Description)"; + class PropertiesChangedCallback : public LLInventoryCallback { public: @@ -128,6 +130,7 @@ LLSidepanelItemInfo::LLSidepanelItemInfo(const LLPanel::Params& p) , mUpdatePendingId(-1) , mIsDirty(false) /*Not ready*/ , mParentFloater(NULL) + , mLabelItemDesc(NULL) { gInventory.addObserver(this); gIdleCallbacks.addFunction(&LLSidepanelItemInfo::onIdle, (void*)this); @@ -158,10 +161,11 @@ bool LLSidepanelItemInfo::postBuild() mItemTypeIcon = getChild("item_type_icon"); mLabelOwnerName = getChild("LabelOwnerName"); mLabelCreatorName = getChild("LabelCreatorName"); + mLabelItemDesc = getChild("LabelItemDesc"); getChild("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); getChild("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this)); - getChild("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); + mLabelItemDesc->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); // Thumnail edition mChangeThumbnailBtn->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onEditThumbnail, this)); // acquired date @@ -923,17 +927,22 @@ void LLSidepanelItemInfo::onCommitDescription() LLViewerInventoryItem* item = findItem(); if(!item) return; - LLTextEditor* labelItemDesc = getChild("LabelItemDesc"); - if(!labelItemDesc) + if(!mLabelItemDesc) { return; } - if((item->getDescription() != labelItemDesc->getText()) && - (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))) + if (!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) { + return; + } + std::string old_desc = item->getDescription(); + std::string new_desc = mLabelItemDesc->getText(); + if(old_desc != new_desc) + { + mLabelItemDesc->setSelectAllOnFocusReceived(false); LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setDescription(labelItemDesc->getText()); + new_item->setDescription(new_desc); onCommitChanges(new_item); } } diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 718edc79d6..0895d3360c 100644 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -46,6 +46,7 @@ class LLObjectInventoryObserver; class LLViewerObject; class LLPermissions; class LLTextBox; +class LLTextEditor; class LLSidepanelItemInfo : public LLPanel, public LLInventoryObserver { @@ -105,6 +106,7 @@ private: LLIconCtrl* mItemTypeIcon; LLTextBox* mLabelOwnerName; LLTextBox* mLabelCreatorName; + LLTextEditor* mLabelItemDesc; // // UI Elements diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index 47f58afa00..223fc2a8f5 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -116,8 +116,8 @@ void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin // needed for handling of any legacy bad data. if (!avatar->getJoint(skin->mJointNames[j])) { - LL_DEBUGS("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL; - LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL; + LL_DEBUGS("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL; + LL_WARNS_ONCE("Avatar") << avatar->getDebugName() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL; skin->mJointNames[j] = "mPelvis"; skin->mJointNumsInitialized = false; // force update after names change. } diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 9e567e3262..6238a1145c 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -71,10 +71,10 @@ LLSLURL::LLSLURL(const std::string& slurl) { LLURI slurl_uri; // parse the slurl as a uri - if (slurl.find(':') == std::string::npos) + if (slurl.find("://") == std::string::npos) { - // There may be no scheme ('secondlife:' etc.) passed in. In that case - // we want to normalize the slurl by putting the appropriate scheme + // There may be no scheme ('secondlife://', 'https://' etc.) passed in. In that + // case we want to normalize the slurl by putting the appropriate scheme // in front of the slurl. So, we grab the appropriate slurl base // from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or // https:///region/ for Standalone grid (the word region, not the region name) diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 68b4ab381a..9b6a87e68d 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1068,7 +1068,7 @@ void LLSnapshotLivePreview::saveTexture(bool outfit_snapshot, std::string name) tid, LLAssetType::AT_TEXTURE, res_name, res_desc, 0, folder_type, inv_type, PERM_ALL, LLFloaterPerms::getGroupPerms("Uploads"), LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost, !outfit_snapshot)); + expected_upload_cost, LLUUID::null, !outfit_snapshot)); upload_new_resource(assetUploadInfo); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 2409b71f00..ba7437798a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -342,13 +342,11 @@ void pump_idle_startup_network(void) { // while there are message to process: // process one then call display_startup() - S32 num_messages = 0; { LockMessageChecker lmc(gMessageSystem); while (lmc.checkAllMessages(gFrameCount, gServicePump)) { display_startup(); - ++num_messages; } lmc.processAcks(); } diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index bb93e2e79e..82c959d7f7 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -161,7 +161,8 @@ bool LLStatusBar::postBuild() getChild("buyL")->setCommitCallback( boost::bind(&LLStatusBar::onClickBuyCurrency, this)); - getChild("goShop")->setCommitCallback(boost::bind(&LLWeb::loadURL, gSavedSettings.getString("MarketplaceURL"), LLStringUtil::null, LLStringUtil::null)); + getChild("goShop")->setCommitCallback( + boost::bind(&LLStatusBar::onClickShop, this)); mBoxBalance = getChild("balance"); mBoxBalance->setClickedCallback(&LLStatusBar::onClickRefreshBalance, this); @@ -520,6 +521,11 @@ void LLStatusBar::onClickBuyCurrency() LLFirstUse::receiveLindens(false); } +void LLStatusBar::onClickShop() +{ + LLFloaterReg::toggleInstanceOrBringToFront("marketplace"); +} + void LLStatusBar::onMouseEnterPresetsCamera() { LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder"); diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index a8fc621ff8..eb4ca8e894 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -101,6 +101,7 @@ public: private: void onClickBuyCurrency(); + void onClickShop(); void onVolumeChanged(const LLSD& newvalue); void onVoiceChanged(const LLSD& newvalue); void onObscureBalanceChanged(const LLSD& newvalue); diff --git a/indra/newview/llteleporthistorystorage.cpp b/indra/newview/llteleporthistorystorage.cpp index dd7c6aa9e3..e02f76711c 100644 --- a/indra/newview/llteleporthistorystorage.cpp +++ b/indra/newview/llteleporthistorystorage.cpp @@ -127,6 +127,12 @@ void LLTeleportHistoryStorage::addItem(const std::string title, const LLVector3d S32 removed_index = -1; if (item_iter != mItems.end()) { + // When teleporting via history it's possible that there can be + // an offset applied to the position, so each new teleport can + // be a meter higher than the last. + // Avoid it by preserving original position. + item.mGlobalPos = item_iter->mGlobalPos; + removed_index = (S32)(item_iter - mItems.begin()); mItems.erase(item_iter); } diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 442c627d07..1a7ce74ccc 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1354,6 +1354,7 @@ U32 LLTextureCache::openAndReadEntries(std::vector& entries) if (bytes_read < sizeof(Entry)) { LL_WARNS() << "Corrupted header entries, failed at " << idx << " / " << num_entries << LL_ENDL; + closeHeaderEntriesFile(); return 0; } entries.push_back(entry); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 20127f5f27..2027e958d5 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -164,7 +164,6 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mFallbackImage(fallback_image), mDefaultImageAssetID(default_image_asset_id), mBlankImageAssetID(blank_image_asset_id), - mTentative(tentative), mAllowNoTexture(allow_no_texture), mLabel(label), mTentativeLabel(NULL), @@ -186,8 +185,10 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mSetImageAssetIDCallback(NULL), mOnUpdateImageStatsCallback(NULL), mBakeTextureEnabled(false), + mLocalTextureEnabled(false), mInventoryPickType(pick_type) { + setTentative(tentative); mCanApplyImmediately = can_apply_immediately; buildFromFile("floater_texture_ctrl.xml"); setCanMinimize(false); @@ -199,7 +200,7 @@ LLFloaterTexturePicker::~LLFloaterTexturePicker() void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selection /*=true*/) { - if( ((mImageAssetID != image_id) || mTentative) && mActive) + if( ((mImageAssetID != image_id) || getTentative()) && mActive) { mNoCopyTextureSelected = false; mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? @@ -277,6 +278,7 @@ void LLFloaterTexturePicker::setImageIDFromItem(const LLInventoryItem* itemp, bo asset_id = BLANK_MATERIAL_ASSET_ID; } setImageID(asset_id, set_selection); + setTentative(false); } void LLFloaterTexturePicker::setActive( bool active ) @@ -525,6 +527,8 @@ bool LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask) void LLFloaterTexturePicker::onOpen(const LLSD& key) { if (sLastPickerMode != 0 + && (mLocalTextureEnabled || sLastPickerMode != 1) + && (mBakeTextureEnabled || sLastPickerMode != 2) && mModeSelector->selectByValue(sLastPickerMode)) { changeMode(); @@ -655,7 +659,7 @@ void LLFloaterTexturePicker::draw() bool valid_dims = updateImageStats(); // if we're inactive, gray out "apply immediate" checkbox - mSelectBtn->setEnabled(mActive && mCanApply && valid_dims); + mSelectBtn->setEnabled(mActive && mCanApply && valid_dims && !getTentative()); mPipetteBtn->setEnabled(mActive); mPipetteBtn->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); @@ -720,9 +724,9 @@ void LLFloaterTexturePicker::draw() mTentativeLabel->setVisible( false ); } - mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); - mBlankBtn->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || mTentative); - mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); + mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || getTentative()); + mBlankBtn->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || getTentative()); + mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || getTentative())); LLFloater::draw(); @@ -775,7 +779,7 @@ void LLFloaterTexturePicker::draw() } // Draw Tentative Label over the image - if( mTentative && !mViewModel->isDirty() ) + if( getTentative() && !mViewModel->isDirty() ) { mTentativeLabel->setVisible( true ); drawChild(mTentativeLabel); @@ -978,6 +982,7 @@ void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata) if (self->mOwner) { self->setImageID( self->getDefaultImageAssetID() ); + self->setTentative(false); } self->commitIfImmediateSet(); } @@ -988,6 +993,7 @@ void LLFloaterTexturePicker::onBtnBlank(void* userdata) LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; self->setCanApply(true, true); self->setImageID( self->getBlankImageAssetID() ); + self->setTentative(false); self->commitIfImmediateSet(); } @@ -998,21 +1004,10 @@ void LLFloaterTexturePicker::onBtnNone(void* userdata) LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; self->setCanApply(true, true); self->setImageID( LLUUID::null ); + self->setTentative(false); self->commitIfImmediateSet(); } -/* -// static -void LLFloaterTexturePicker::onBtnRevert(void* userdata) -{ - LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - self->setImageID( self->mOriginalImageAssetID ); - // TODO: Change this to tell the owner to cancel. It needs to be - // smart enough to restore multi-texture selections. - self->mOwner->onFloaterCommit(); - self->mViewModel->resetDirty(); -}*/ - // static void LLFloaterTexturePicker::onBtnCancel(void* userdata) { @@ -1219,6 +1214,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata) if (self->mSetImageAssetIDCallback) { self->mSetImageAssetIDCallback(inworld_id); + self->setTentative(false); } if (self->childGetValue("apply_immediate_check").asBoolean()) @@ -1297,6 +1293,7 @@ void LLFloaterTexturePicker::onBakeTextureSelect(LLUICtrl* ctrl, void *user_data } self->setImageID(imageID); + self->setTentative(false); self->mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? if (!self->mPreviewSettingChanged) @@ -1317,7 +1314,7 @@ void LLFloaterTexturePicker::onBakeTextureSelect(LLUICtrl* ctrl, void *user_data void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image) { - mSelectBtn->setEnabled(can_apply); + mSelectBtn->setEnabled(can_apply && !getTentative()); // will be updated on draw getChildRef("preview_disabled").setVisible(!can_preview && inworld_image); getChildRef("apply_immediate_check").setVisible(can_preview); @@ -1491,7 +1488,13 @@ void LLFloaterTexturePicker::refreshInventoryFilter() void LLFloaterTexturePicker::setLocalTextureEnabled(bool enabled) { + mLocalTextureEnabled = enabled; mModeSelector->setEnabledByValue(1, enabled); + if (!enabled && (mModeSelector->getValue().asInteger() == 2)) + { + mModeSelector->selectByValue(0); + onModeSelect(0, this); + } } void LLFloaterTexturePicker::setBakeTextureEnabled(bool enabled) @@ -1618,6 +1621,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te ) else { setImageID(te.getID()); + setTentative(false); } mNoCopyTextureSelected = false; @@ -1831,6 +1835,17 @@ void LLTextureCtrl::clear() setImageAssetID(LLUUID::null); } +void LLTextureCtrl::setTentative(bool tentative) +{ + LLFloater* floaterp = mFloaterHandle.get(); + + if (floaterp) + { + floaterp->setTentative(tentative); + } + LLUICtrl::setTentative(tentative); +} + void LLTextureCtrl::setLabel(const std::string& label) { mLabel = label; @@ -1878,11 +1893,9 @@ void LLTextureCtrl::showPicker(bool take_focus) if (texture_floaterp) { texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4, _5)); - } - if (texture_floaterp) - { texture_floaterp->setSetImageAssetIDCallback(boost::bind(&LLTextureCtrl::setImageAssetID, this, _1)); + texture_floaterp->setLocalTextureEnabled(mAllowLocalTexture); texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled && mInventoryPickType != PICK_MATERIAL); } @@ -1892,12 +1905,6 @@ void LLTextureCtrl::showPicker(bool take_focus) floaterp->openFloater(); } - LLFloaterTexturePicker* picker_floater = dynamic_cast(floaterp); - if (picker_floater) - { - picker_floater->setLocalTextureEnabled(mAllowLocalTexture); - } - if (take_focus) { floaterp->setFocus(true); @@ -2077,7 +2084,17 @@ void LLTextureCtrl::setOnTextureSelectedCallback(texture_selected_callback cb) } } -void LLTextureCtrl::setImageAssetName(const std::string& name) +void LLTextureCtrl::setAllowLocalTexture(bool b) +{ + mAllowLocalTexture = b; + LLFloaterTexturePicker* picker_floater = dynamic_cast(mFloaterHandle.get()); + if (picker_floater) + { + picker_floater->setLocalTextureEnabled(mAllowLocalTexture); + } +} + +void LLTextureCtrl::setImageAssetName(const std::string& name) { LLPointer imagep = LLUI::getUIImage(name); if(imagep) @@ -2102,6 +2119,7 @@ void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id ) if( floaterp && getEnabled() ) { floaterp->setImageID( asset_id ); + floaterp->setTentative(getTentative()); floaterp->resetDirty(); } } diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index df5e763139..79957431b7 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -167,6 +167,8 @@ public: // LLUICtrl interface void clear() override; + void setTentative(bool b) override; + // Takes a UUID, wraps get/setImageAssetID void setValue(const LLSD& value) override; LLSD getValue() const override; @@ -181,7 +183,7 @@ public: void setAllowNoTexture( bool b ) { mAllowNoTexture = b; } bool getAllowNoTexture() const { return mAllowNoTexture; } - void setAllowLocalTexture(bool b) { mAllowLocalTexture = b; } + void setAllowLocalTexture(bool b); bool getAllowLocalTexture() const { return mAllowLocalTexture; } const LLUUID& getImageItemID() { return mImageItemID; } @@ -405,7 +407,6 @@ protected: LLUIImagePtr mFallbackImage; // What to show if currently selected texture is null. LLUUID mDefaultImageAssetID; LLUUID mBlankImageAssetID; - bool mTentative; bool mAllowNoTexture; LLUUID mSpecialCurrentImageAssetID; // Used when the asset id has no corresponding texture in the user's inventory. LLUUID mOriginalImageAssetID; @@ -456,6 +457,7 @@ private: set_on_update_image_stats_callback mOnUpdateImageStatsCallback; bool mBakeTextureEnabled; + bool mLocalTextureEnabled; static S32 sLastPickerMode; }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 087761cbd0..93b5806acf 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -49,6 +49,7 @@ #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" +#include "llviewerthrottle.h" #include "llviewerassetstats.h" #include "llworld.h" #include "llsdparam.h" @@ -2434,7 +2435,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded, bool qa_mod mOriginFetchSource(LLTextureFetch::FROM_ALL), mTextureInfoMainThread(false) { - mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + mMaxBandwidth = LLViewerThrottle::getMaxBandwidthKbps(); mTextureInfo.setLogging(true); LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); @@ -2484,7 +2485,7 @@ LLTextureFetch::~LLTextureFetch() } S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http) + S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http) { LL_PROFILE_ZONE_SCOPED; if (mDebugPause) @@ -2496,13 +2497,13 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L { LL_DEBUGS("Avatar") << " requesting " << id << " " << w << "x" << h << " discard " << desired_discard << " type " << f_type << LL_ENDL; } - LLTextureFetchWorker* worker = getWorker(id) ; + LLTextureFetchWorker* worker = getWorker(id); if (worker) { if (worker->mHost != host) { LL_WARNS(LOG_TXT) << "LLTextureFetch::createRequest " << id << " called with multiple hosts: " - << host << " != " << worker->mHost << LL_ENDL; + << host << " != " << worker->mHost << LL_ENDL; removeRequest(worker, true); worker = NULL; return CREATE_REQUEST_ERROR_MHOSTS; @@ -2538,7 +2539,7 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L // we really do get it.) desired_size = MAX_IMAGE_DATA_SIZE; } - else if (w*h*c > 0) + else if (w * h * c > 0) { // If the requester knows the dimensions of the image, // this will calculate how much data we need without having to parse the header @@ -2596,14 +2597,15 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; - worker->setCanUseHTTP(can_use_http) ; + worker->setCanUseHTTP(can_use_http); worker->unlockWorkMutex(); // -Mw } LL_DEBUGS(LOG_TXT) << "REQUESTED: " << id << " f_type " << fttype_to_string(f_type) - << " Discard: " << desired_discard << " size " << desired_size << LL_ENDL; + << " Discard: " << desired_discard << " size " << desired_size << LL_ENDL; return desired_discard; } + // Threads: T* // // protected @@ -2952,11 +2954,10 @@ void LLTextureFetch::commonUpdate() size_t LLTextureFetch::update(F32 max_time_ms) { LL_PROFILE_ZONE_SCOPED; - static LLCachedControl band_width(gSavedSettings,"ThrottleBandwidthKBPS", 3000.0); { mNetworkQueueMutex.lock(); // +Mfnq - mMaxBandwidth = band_width(); + mMaxBandwidth = LLViewerThrottle::getMaxBandwidthKbps(); add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, mHTTPTextureBits); mHTTPTextureBits = (U32Bits)0; diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 8ab90896dc..851d6c11a0 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -85,8 +85,9 @@ public: }; // Threads: T* (but Tmain mostly) + // returns discard on success, fail code otherwise S32 createRequest(FTType f_type, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); // Requests that a fetch operation be deleted from the queue. // If @cancel is true, also stops any I/O operations pending. diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 8560a01c4b..8cbede8303 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -49,6 +49,7 @@ #include "llviewerobjectlist.h" #include "llviewertexture.h" #include "llviewertexturelist.h" +#include "llviewerthrottle.h" #include "llviewerwindow.h" #include "llwindow.h" #include "llvovolume.h" @@ -633,7 +634,7 @@ void LLGLTexMemBar::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &x_right); F32Kilobits bandwidth(LLAppViewer::getTextureFetch()->getTextureBandwidth()); - F32Kilobits max_bandwidth(gSavedSettings.getF32("ThrottleBandwidthKBPS")); + F32Kilobits max_bandwidth(LLViewerThrottle::getMaxBandwidthKbps()); 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.value(), max_bandwidth.value()); diff --git a/indra/newview/llthumbnailctrl.cpp b/indra/newview/llthumbnailctrl.cpp index ae21d3e733..b077262f06 100644 --- a/indra/newview/llthumbnailctrl.cpp +++ b/indra/newview/llthumbnailctrl.cpp @@ -111,7 +111,9 @@ void LLThumbnailCtrl::draw() gl_draw_scaled_image( draw_rect.mLeft, draw_rect.mBottom, draw_rect.getWidth(), draw_rect.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); - mTexturep->setKnownDrawSize(draw_rect.getWidth(), draw_rect.getHeight()); + // Thumbnails are usually 256x256 or smaller, either report that or + // some high value to get image with higher priority + mTexturep->setKnownDrawSize(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE); } else if( mImagep.notNull() ) { @@ -238,12 +240,8 @@ void LLThumbnailCtrl::initImage() { // Should it support baked textures? mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_THUMBNAIL); - mTexturep->forceToSaveRawImage(0); - - S32 desired_draw_width = MAX_IMAGE_SIZE; - S32 desired_draw_height = MAX_IMAGE_SIZE; - mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height); + mTexturep->setKnownDrawSize(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE); } } else if (tvalue.isString()) diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 84854a79d4..0f871dc1bb 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -421,7 +421,7 @@ void LLToast::setVisible(bool show) if(mHideBtn) mHideBtn->setVisible(show); } - LLFloater::setVisible(show); + LLModalDialog::setVisible(show); if (mPanel && !mPanel->isDead() && mWrapperPanel diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index ff4fcc2b0b..b4a5955be3 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1125,28 +1125,33 @@ void set_texture_to_material(LLViewerObject* hit_obj, case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR: default: { - material->setBaseColorId(asset_id); + material->setBaseColorId(asset_id, true); } break; case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS: { - material->setOcclusionRoughnessMetallicId(asset_id); + material->setOcclusionRoughnessMetallicId(asset_id, true); } break; case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE: { - material->setEmissiveId(asset_id); + material->setEmissiveId(asset_id, true); } break; case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL: { - material->setNormalId(asset_id); + material->setNormalId(asset_id, true); } break; } + // Update viewer side, needed for updating mSavedGLTFOverrideMaterials. + // Also for parity, we are immediately setting textures and materials, + // so we should immediate set overrides to. + hit_obj->setTEGLTFMaterialOverride(hit_face, material); + // update server LLGLTFMaterialList::queueModify(hit_obj, hit_face, material); } @@ -2156,7 +2161,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( { if(mSource == SOURCE_LIBRARY) { - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, false)); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), @@ -2167,7 +2172,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( } else { - rez_attachment(item, 0); + rez_attachment(item, 0, false); } } return ACCEPT_YES_SINGLE; diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 0fd9faab35..0a69be528f 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -1549,7 +1549,13 @@ bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool mo } // Further object detail required beyond this point - LLPermissions* perms = LLSelectMgr::getInstance()->getHoverNode()->mPermissions; + LLSelectNode* hover_node = LLSelectMgr::instance().getHoverNode(); + if (hover_node == nullptr) + { + LL_WARNS() << "No Hover node" << LL_ENDL; + return false; + } + LLPermissions* perms = hover_node->mPermissions; if(perms == nullptr) { LL_WARNS() << "LLSelectMgr::getInstance()->getHoverNode()->mPermissions is NULL" << LL_ENDL; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 7ef2c8d697..7d5386110d 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -62,7 +62,8 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, LLAssetType::EType assetType, std::string name, std::string description, S32 compressionInfo, LLFolderType::EType destinationType, LLInventoryType::EType inventoryType, U32 nextOWnerPerms, - U32 groupPerms, U32 everyonePerms, S32 expectedCost, bool showInventory) : + U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId, bool showInventory) : mTransactionId(transactId), mAssetType(assetType), mName(name), @@ -75,7 +76,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, mEveryonePerms(everyonePerms), mExpectedUploadCost(expectedCost), mShowInventory(showInventory), - mFolderId(LLUUID::null), + mFolderId(destFolderId), mItemId(LLUUID::null), mAssetId(LLAssetID::null) { } @@ -84,7 +85,8 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId, LLResourceUploadInfo::LLResourceUploadInfo(std::string name, std::string description, S32 compressionInfo, LLFolderType::EType destinationType, LLInventoryType::EType inventoryType, - U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, S32 expectedCost, bool showInventory) : + U32 nextOWnerPerms, U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId, bool showInventory) : mName(name), mDescription(description), mCompressionInfo(compressionInfo), @@ -97,7 +99,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(std::string name, mShowInventory(showInventory), mTransactionId(), mAssetType(LLAssetType::AT_NONE), - mFolderId(LLUUID::null), + mFolderId(destFolderId), mItemId(LLUUID::null), mAssetId(LLAssetID::null) { @@ -306,10 +308,10 @@ void LLResourceUploadInfo::assignDefaults() } else { - mFolderId = gInventory.findUserDefinedCategoryUUIDForType( - (mDestinationFolderType == LLFolderType::FT_NONE) ? - (LLFolderType::EType)mAssetType : mDestinationFolderType); -} + mFolderId = gInventory.findUserDefinedCategoryUUIDForType( + (mDestinationFolderType == LLFolderType::FT_NONE) ? + (LLFolderType::EType)mAssetType : mDestinationFolderType); + } } std::string LLResourceUploadInfo::getDisplayName() const @@ -366,10 +368,12 @@ LLNewFileResourceUploadInfo::LLNewFileResourceUploadInfo( U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId, bool show_inventory) : LLResourceUploadInfo(name, description, compressionInfo, destinationType, inventoryType, - nextOWnerPerms, groupPerms, everyonePerms, expectedCost, show_inventory), + nextOWnerPerms, groupPerms, everyonePerms, expectedCost, + destFolderId, show_inventory), mFileName(fileName), mMaxImageSize(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT) { @@ -580,12 +584,13 @@ LLNewBufferedResourceUploadInfo::LLNewBufferedResourceUploadInfo( U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId, bool show_inventory, uploadFinish_f finish, uploadFailure_f failure) : LLResourceUploadInfo(name, description, compressionInfo, destinationType, inventoryType, - nextOWnerPerms, groupPerms, everyonePerms, expectedCost, show_inventory) + nextOWnerPerms, groupPerms, everyonePerms, expectedCost, destFolderId, show_inventory) , mBuffer(buffer) , mFinishFn(finish) , mFailureFn(failure) diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h index 365436ede0..c627e9dbb8 100644 --- a/indra/newview/llviewerassetupload.h +++ b/indra/newview/llviewerassetupload.h @@ -54,6 +54,7 @@ public: U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID &destFolderId = LLUUID::null, bool showInventory = true); virtual ~LLResourceUploadInfo() @@ -104,6 +105,7 @@ protected: U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId = LLUUID::null, bool showInventory = true); LLResourceUploadInfo( @@ -155,6 +157,7 @@ public: U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID &destFolderId = LLUUID::null, bool show_inventory = true); virtual LLSD prepareUpload(); @@ -193,6 +196,7 @@ public: U32 groupPerms, U32 everyonePerms, S32 expectedCost, + const LLUUID& destFolderId, // use null for default bool show_inventory, uploadFinish_f finish, uploadFailure_f failure); @@ -219,6 +223,7 @@ public: typedef std::function taskUploadFinish_f; typedef std::function uploadFailed_f; + // destFolderId is the folder to put the new item in, leave null for default LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType::EType assetType, std::string buffer, invnUploadFinish_f finish, uploadFailed_f failed); LLBufferedAssetUploadInfo(LLUUID itemId, LLPointer image, invnUploadFinish_f finish); LLBufferedAssetUploadInfo(LLUUID taskId, LLUUID itemId, LLAssetType::EType assetType, std::string buffer, taskUploadFinish_f finish, uploadFailed_f failed); diff --git a/indra/newview/llviewerattachmenu.cpp b/indra/newview/llviewerattachmenu.cpp index f2557e299c..9828ab1fdf 100644 --- a/indra/newview/llviewerattachmenu.cpp +++ b/indra/newview/llviewerattachmenu.cpp @@ -116,12 +116,12 @@ void LLViewerAttachMenu::attachObjects(const uuid_vec_t& items, const std::strin LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getLinkedItem(id); if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) { - rez_attachment(item, attachmentp); // don't replace if called from an "Attach To..." menu + rez_attachment(item, attachmentp, false); // don't replace if called from an "Attach To..." menu } else if(item && item->isFinished()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, attachmentp)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, attachmentp, false)); copy_inventory_item(gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index 89c28ee2f9..511293a808 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -83,18 +83,16 @@ LLViewerCamera::LLViewerCamera() : LLCamera() } } -void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, const LLVector3 &up_direction, const LLVector3 &point_of_interest) +bool LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, const LLVector3 &up_direction, const LLVector3 &point_of_interest) { // do not update if avatar didn't move if (!LLViewerJoystick::getInstance()->getCameraNeedsUpdate()) { - return; + return true; } - LLVector3 last_position; - LLVector3 last_axis; - last_position = getOrigin(); - last_axis = getAtAxis(); + LLVector3 last_position = getOrigin(); + LLVector3 last_axis = getAtAxis(); mLastPointOfInterest = point_of_interest; @@ -104,30 +102,49 @@ void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, const LLVecto regp = gAgent.getRegion(); } - F32 water_height = (NULL != regp) ? regp->getWaterHeight() : 0.f; + F32 water_height = regp ? regp->getWaterHeight() : 0.f; LLVector3 origin = center; + // Move origin[VZ] far enough (up or down) from the water surface + static const F32 MIN_DIST_TO_WATER = 0.2f; + F32& zpos = origin.mV[VZ]; + if (zpos < water_height + MIN_DIST_TO_WATER) { - if (origin.mV[2] > water_height) + if (zpos >= water_height) { - origin.mV[2] = llmax(origin.mV[2], water_height + 0.20f); + zpos = water_height + MIN_DIST_TO_WATER; } - else + else if (zpos > water_height - MIN_DIST_TO_WATER) { - origin.mV[2] = llmin(origin.mV[2], water_height - 0.20f); + zpos = water_height - MIN_DIST_TO_WATER; } } - setOriginAndLookAt(origin, up_direction, point_of_interest); + LLVector3 at(point_of_interest - origin); + at.normalize(); + if (at.isNull() || !at.isFinite()) + return false; + + LLVector3 left(up_direction % at); + left.normalize(); + if (left.isNull() || !left.isFinite()) + return false; + + LLVector3 up = at % left; + up.normalize(); + if (up.isNull() || !up.isFinite()) + return false; + + setOrigin(origin); + setAxes(at, left, up); mVelocityDir = origin - last_position ; F32 dpos = mVelocityDir.normVec() ; LLQuaternion rotation; rotation.shortestArc(last_axis, getAtAxis()); - F32 x, y, z; - F32 drot; + F32 drot, x, y, z; rotation.getAngleAxis(&drot, &x, &y, &z); add(sVelocityStat, dpos); @@ -140,6 +157,8 @@ void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, const LLVecto // update pixel meter ratio using default fov, not modified one mPixelMeterRatio = (F32)(getViewHeightInPixels()/ (2.f*tanf(mCameraFOVDefault*0.5f))); // update screen pixel area + + return true; mScreenPixelArea =(S32)((F32)getViewHeightInPixels() * ((F32)getViewHeightInPixels() * getAspect())); } @@ -147,7 +166,6 @@ const LLMatrix4 &LLViewerCamera::getProjection() const { calcProjection(getFar()); return mProjectionMatrix; - } const LLMatrix4 &LLViewerCamera::getModelview() const @@ -160,13 +178,12 @@ const LLMatrix4 &LLViewerCamera::getModelview() const void LLViewerCamera::calcProjection(const F32 far_distance) const { - F32 fov_y, z_far, z_near, aspect, f; - fov_y = getView(); - z_far = far_distance; - z_near = getNear(); - aspect = getAspect(); + F32 fov_y = getView(); + F32 z_far = far_distance; + F32 z_near = getNear(); + F32 aspect = getAspect(); - f = 1/tan(fov_y*0.5f); + F32 f = 1 / tan(fov_y * 0.5f); mProjectionMatrix.setZero(); mProjectionMatrix.mMatrix[0][0] = f/aspect; @@ -272,9 +289,9 @@ void LLViewerCamera::updateFrustumPlanes(LLCamera& camera, bool ortho, bool zfli } void LLViewerCamera::setPerspective(bool for_selection, - S32 x, S32 y_from_bot, S32 width, S32 height, - bool limit_select_distance, - F32 z_near, F32 z_far) + S32 x, S32 y_from_bot, S32 width, S32 height, + bool limit_select_distance, + F32 z_near, F32 z_far) { F32 fov_y, aspect; fov_y = getView(); @@ -336,7 +353,7 @@ void LLViewerCamera::setPerspective(bool for_selection, { float offset = mZoomFactor - 1.f; int pos_y = mZoomSubregion / llceil(mZoomFactor); - int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor)); + int pos_x = mZoomSubregion - (pos_y * llceil(mZoomFactor)); glm::mat4 translate; translate = glm::translate(glm::vec3(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f)); @@ -349,7 +366,7 @@ void LLViewerCamera::setPerspective(bool for_selection, calcProjection(z_far); // Update the projection matrix cache - proj_mat *= glm::perspective(fov_y,aspect,z_near,z_far); + proj_mat *= glm::perspective(fov_y, aspect, z_near, z_far); gGL.loadMatrix(glm::value_ptr(proj_mat)); @@ -357,7 +374,7 @@ void LLViewerCamera::setPerspective(bool for_selection, gGL.matrixMode(LLRender::MM_MODELVIEW); - glm::mat4 modelview(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); + glm::mat4 modelview(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); GLfloat ogl_matrix[16]; @@ -373,9 +390,9 @@ void LLViewerCamera::setPerspective(bool for_selection, // however, it is also unused (the GL matricies are used for selection, (see LLCamera::sphereInFrustum())) and so i'm not // comfortable hacking on it. calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidthScaled() - 0.5f, - (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f, - (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidthScaled() - 0.5f, - (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f); + (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f, + (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidthScaled() - 0.5f, + (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeightScaled() - 0.5f); } @@ -389,7 +406,6 @@ void LLViewerCamera::setPerspective(bool for_selection, updateFrustumPlanes(*this); } - // Uses the last GL matrices set in set_perspective to project a point from // screen coordinates to the agent's region. void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h index 91d26f09f2..cb0994be0a 100644 --- a/indra/newview/llviewercamera.h +++ b/indra/newview/llviewercamera.h @@ -60,7 +60,7 @@ public: static eCameraID sCurCameraID; - void updateCameraLocation(const LLVector3 ¢er, + bool updateCameraLocation(const LLVector3 ¢er, const LLVector3 &up_direction, const LLVector3 &point_of_interest); @@ -75,12 +75,12 @@ 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; + F32 getCosHalfFov() const { return mCosHalfCameraFOV; } + F32 getAverageSpeed() const { return mAverageSpeed; } + F32 getAverageAngularSpeed() const { return mAverageAngularSpeed; } LLVector3 getVelocityDir() const {return mVelocityDir;} static LLTrace::CountStatHandle<>* getVelocityStat() {return &sVelocityStat; } static LLTrace::CountStatHandle<>* getAngularVelocityStat() {return &sAngularVelocityStat; } - 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); @@ -88,21 +88,21 @@ public: // Sets the current matrix /* virtual */ void setView(F32 vertical_fov_rads); // NOTE: broadcasts to simulator void setViewNoBroadcast(F32 vertical_fov_rads); // set FOV without broadcasting to simulator (for temporary local cameras) + F32 getDefaultFOV() const { return mCameraFOVDefault; } void setDefaultFOV(F32 fov) ; - F32 getDefaultFOV() { return mCameraFOVDefault; } bool isDefaultFOVChanged(); bool cameraUnderWater() const; bool areVertsVisible(LLViewerObject* volumep, bool all_verts); - const LLVector3 &getPointOfInterest() { return mLastPointOfInterest; } + const LLVector3& getPointOfInterest() const { return mLastPointOfInterest; } F32 getPixelMeterRatio() const { return mPixelMeterRatio; } S32 getScreenPixelArea() const { return mScreenPixelArea; } void setZoomParameters(F32 factor, S16 subregion) { mZoomFactor = factor; mZoomSubregion = subregion; } - F32 getZoomFactor() { return mZoomFactor; } - S16 getZoomSubRegion() { return mZoomSubregion; } + F32 getZoomFactor() const { return mZoomFactor; } + S16 getZoomSubRegion() const { return mZoomSubregion; } protected: void calcProjection(const F32 far_distance) const; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4b3af6d7e8..3b35ca8db1 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -21,6 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -98,6 +99,7 @@ #include "llfloaterlinkreplace.h" #include "llfloaterloadprefpreset.h" #include "llfloatermap.h" +#include "llfloatermarketplace.h" #include "llfloatermarketplacelistings.h" #include "llfloatermediasettings.h" #include "llfloatermemleak.h" @@ -419,6 +421,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("media_settings", "floater_media_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("marketplace", "floater_marketplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("marketplace_listings", "floater_marketplace_listings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("marketplace_validation", "floater_marketplace_validation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index e2022cae37..6155058f14 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -71,6 +71,9 @@ #include "llclipboard.h" #include "llhttpretrypolicy.h" #include "llsettingsvo.h" +#include "llinventorylistener.h" + +LLInventoryListener sInventoryListener; // do-nothing ops for use in callbacks. void no_op_inventory_func(const LLUUID&) {} @@ -751,27 +754,30 @@ S32 LLViewerInventoryCategory::getViewerDescendentCount() const return descendents_actual; } -LLSD LLViewerInventoryCategory::exportLLSD() const +void LLViewerInventoryCategory::exportLLSD(LLSD & cat_data) const { - LLSD cat_data = LLInventoryCategory::exportLLSD(); + LLInventoryCategory::exportLLSD(cat_data); cat_data[INV_OWNER_ID] = mOwnerID; cat_data[INV_VERSION] = mVersion; - - return cat_data; } -bool LLViewerInventoryCategory::importLLSD(const LLSD& cat_data) +bool LLViewerInventoryCategory::importLLSD(const std::string& label, const LLSD& value) { - LLInventoryCategory::importLLSD(cat_data); - if (cat_data.has(INV_OWNER_ID)) + if (LLInventoryCategory::importLLSD(label, value)) { - mOwnerID = cat_data[INV_OWNER_ID].asUUID(); + return true; } - if (cat_data.has(INV_VERSION)) + else if (label == INV_OWNER_ID) { - setVersion(cat_data[INV_VERSION].asInteger()); + mOwnerID = value.asUUID(); + return true; } - return true; + else if (label == INV_VERSION) + { + setVersion(value.asInteger()); + return true; + } + return false; } bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item) @@ -966,7 +972,7 @@ void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id) } } -void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp) +void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace) { if (inv_item.isNull()) return; @@ -974,7 +980,7 @@ void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachme LLViewerInventoryItem *item = gInventory.getItem(inv_item); if (item) { - rez_attachment(item, attachmentp); + rez_attachment(item, attachmentp, replace); } } @@ -1436,7 +1442,8 @@ void update_inventory_category( if(obj) { if (LLFolderType::lookupIsProtectedType(obj->getPreferredType()) - && (updates.size() != 1 || !updates.has("thumbnail"))) + && (updates.size() != 1 + || !(updates.has("thumbnail") || updates.has("favorite")))) { LLNotificationsUtil::add("CannotModifyProtectedCategories"); return; diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 21a6606253..0dfbf0cced 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -232,8 +232,8 @@ public: // How many descendents do we currently have information for in the InventoryModel? S32 getViewerDescendentCount() const; - LLSD exportLLSD() const; - bool importLLSD(const LLSD& cat_data); + virtual void exportLLSD(LLSD &sd) const; + virtual bool importLLSD(const std::string& label, const LLSD& value); void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); @@ -264,7 +264,7 @@ public: class LLViewerJointAttachment; -void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp); +void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace); void activate_gesture_cb(const LLUUID& inv_item); diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 7543fb3743..9d45ea24b9 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -881,6 +881,10 @@ void LLViewerJoystick::moveObjects(bool reset) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } if (sDelta[0] || sDelta[1] || sDelta[2]) { @@ -1055,6 +1059,10 @@ void LLViewerJoystick::moveAvatar(bool reset) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } setCameraNeedsUpdate(true); } @@ -1267,10 +1275,17 @@ void LLViewerJoystick::moveFlycam(bool reset) } // Clear AFK state if moved beyond the deadzone - if (!is_zero && gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + if (!is_zero) + { + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } + } sFlycamPosition += LLVector3(sDelta) * sFlycamRotation; @@ -1331,6 +1346,10 @@ bool LLViewerJoystick::toggleFlycam() { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } mOverrideCamera = !mOverrideCamera; if (mOverrideCamera) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 82a52e63f6..c6bc252efd 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1259,35 +1259,46 @@ void LLViewerMedia::getOpenIDCookieCoro(std::string url) { LLAppViewer::instance()->postToMainCoro([=]() { - LLMediaCtrl* media_instance = LLFloaterReg::getInstance("destinations")->getChild("destination_guide_contents"); - if (media_instance) + std::string cookie_host = authority.substr(hostStart, hostEnd - hostStart); + std::string cookie_name = ""; + std::string cookie_value = ""; + std::string cookie_path = ""; + bool httponly = true; + bool secure = true; + + LLViewerMedia* inst = getInstance(); + if (inst->parseRawCookie(inst->mOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure)) { - LLViewerMedia* inst = getInstance(); - std::string cookie_host = authority.substr(hostStart, hostEnd - hostStart); - std::string cookie_name = ""; - std::string cookie_value = ""; - std::string cookie_path = ""; - bool httponly = true; - bool secure = true; - if (inst->parseRawCookie(inst->mOpenIDCookie, cookie_name, cookie_value, cookie_path, httponly, secure) && - media_instance->getMediaPlugin()) + // MAINT-5711 - inexplicably, the CEF setCookie function will no longer set the cookie if the + // url and domain are not the same. This used to be my.sl.com and id.sl.com respectively and worked. + // For now, we use the URL for the OpenID POST request since it will have the same authority + // as the domain field. + // (Feels like there must be a less dirty way to construct a URL from component LLURL parts) + // MAINT-6392 - Rider: Do not change, however, the original URI requested, since it is used further + // down. + std::string cefUrl(std::string(inst->mOpenIDURL.mURI) + "://" + std::string(inst->mOpenIDURL.mAuthority)); + + // list of floater names and webbrowser therein to set the cookie that arrived via login into + struct MediaCookieInstance { + std::string floater_name; + std::string browser_name; + }; + struct MediaCookieInstance media_cookie_instances[] = { + {"search", "search_contents" }, + {"marketplace", "marketplace_contents" }, + {"destinations", "destination_guide_contents" }, + }; + for (MediaCookieInstance mci : media_cookie_instances) { - // MAINT-5711 - inexplicably, the CEF setCookie function will no longer set the cookie if the - // url and domain are not the same. This used to be my.sl.com and id.sl.com respectively and worked. - // For now, we use the URL for the OpenID POST request since it will have the same authority - // as the domain field. - // (Feels like there must be a less dirty way to construct a URL from component LLURL parts) - // MAINT-6392 - Rider: Do not change, however, the original URI requested, since it is used further - // down. - std::string cefUrl(std::string(inst->mOpenIDURL.mURI) + "://" + std::string(inst->mOpenIDURL.mAuthority)); + LLMediaCtrl* media_instance = LLFloaterReg::getInstance(mci.floater_name)->getChild(mci.browser_name); + if (media_instance && media_instance->getMediaPlugin()) + { + media_instance->getMediaPlugin()->setCookie(cefUrl, cookie_name, cookie_value, cookie_host, + cookie_path, httponly, secure); - media_instance->getMediaPlugin()->setCookie(cefUrl, cookie_name, cookie_value, cookie_host, - cookie_path, httponly, secure); - - // Now that we have parsed the raw cookie, we must store it so that each new media instance - // can also get a copy and faciliate logging into internal SL sites. - media_instance->getMediaPlugin()->storeOpenIDCookie(cefUrl, cookie_name, cookie_value, - cookie_host, cookie_path, httponly, secure); + media_instance->getMediaPlugin()->storeOpenIDCookie(cefUrl, cookie_name, cookie_value, + cookie_host, cookie_path, httponly, secure); + } } } }); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 44157d2d2d..f7688b762f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -85,6 +85,7 @@ #include "lltoolface.h" #include "llhints.h" #include "llhudeffecttrail.h" +#include "llhudeffectresetskeleton.h" #include "llhudmanager.h" #include "llimview.h" #include "llinventorybridge.h" @@ -1847,7 +1848,6 @@ class LLAdvancedAppearanceToXML : public view_listener_t { bool handleEvent(const LLSD& userdata) { - std::string emptyname; LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); LLVOAvatar *avatar = NULL; if (obj) @@ -1874,7 +1874,7 @@ class LLAdvancedAppearanceToXML : public view_listener_t } if (avatar) { - avatar->dumpArchetypeXML(emptyname); + avatar->dumpArchetypeXML(LLStringUtil::null); } return true; } @@ -4354,13 +4354,6 @@ void set_god_level(U8 god_level) // changing god-level can affect which menus we see show_debug_menus(); - - // changing god-level can invalidate search results - LLFloaterSearch *search = dynamic_cast(LLFloaterReg::getInstance("search")); - if (search) - { - search->godLevelChanged(god_level); - } } #ifdef TOGGLE_HACKED_GODLIKE_VIEWER @@ -5644,6 +5637,38 @@ class LLToolsEnablePathfindingRebakeRegion : public view_listener_t } }; +class LLToolsCheckSelectionLODMode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string param = userdata.asString(); + static LLCachedControl debug_selection_lods(gSavedSettings, "DebugSelectionLODs", 0); + if ("default" == param) + { + return debug_selection_lods() < 0; + } + else if ("high" == param) + { + return debug_selection_lods() == 3; + } + else if ("medium" == param) + { + return debug_selection_lods() == 2; + } + else if ("low" == param) + { + return debug_selection_lods() == 1; + } + else if ("lowest" == param) + { + return debug_selection_lods() == 0; + } + + return false; + } +}; + + // Round the position of all root objects to the grid class LLToolsSnapObjectXY : public view_listener_t { @@ -6485,7 +6510,7 @@ void handle_look_at_selection(const LLSD& param) } } -void handle_zoom_to_object(const LLUUID& object_id) +bool handle_zoom_to_object(const LLUUID& object_id) { const F32 PADDING_FACTOR = 2.f; @@ -6503,12 +6528,14 @@ void handle_zoom_to_object(const LLUUID& object_id) obj_to_cam.normVec(); - LLVector3d object_center_global = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); + LLVector3d object_center_global = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); - gAgentCamera.setCameraPosAndFocusGlobal(object_center_global + LLVector3d(obj_to_cam * distance), + gAgentCamera.setCameraPosAndFocusGlobal(object_center_global + LLVector3d(obj_to_cam * distance), object_center_global, object_id ); + return true; } + return false; } class LLAvatarInviteToGroup : public view_listener_t @@ -6619,8 +6646,18 @@ class LLAvatarResetSkeleton : public view_listener_t { if (LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject())) { + if(avatar->getID() == gAgentID) + { + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(false); + } + else + { avatar->resetSkeleton(false); } + } return true; } }; @@ -6643,8 +6680,18 @@ class LLAvatarResetSkeletonAndAnimations : public view_listener_t { if (LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject())) { + if(avatar->getID() == gAgentID) + { + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(true); + } + else + { avatar->resetSkeleton(true); } + } return true; } }; @@ -6671,11 +6718,24 @@ class LLAvatarResetSelfSkeletonAndAnimations : public view_listener_t { if (LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject())) { - avatar->resetSkeleton(true); + if(avatar->getID() == gAgentID) + { + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject((LLViewerObject*)avatar); + effectp->setResetAnimations(true); } else { - gAgentAvatarp->resetSkeleton(true); + avatar->resetSkeleton(true); + } + } + else + { + LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true); + effectp->setSourceObject(gAgentAvatarp); + effectp->setTargetObject(gAgentAvatarp); + effectp->setResetAnimations(true); } return true; } @@ -8468,6 +8528,36 @@ class LLToolsSelectBySurrounding : public view_listener_t } }; +class LLToolsSelectionLODMode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string param = userdata.asString(); + if ("default" == param) + { + gSavedSettings.setS32("DebugSelectionLODs", -1); + } + else if ("high" == param) + { + gSavedSettings.setS32("DebugSelectionLODs", 3); + } + else if ("medium" == param) + { + gSavedSettings.setS32("DebugSelectionLODs", 2); + } + else if ("low" == param) + { + gSavedSettings.setS32("DebugSelectionLODs", 1); + } + else if ("lowest" == param) + { + gSavedSettings.setS32("DebugSelectionLODs", 0); + } + + return true; + } +}; + class LLToolsShowHiddenSelection : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9707,6 +9797,7 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsSelectInvisibleObjects(), "Tools.SelectInvisibleObjects"); view_listener_t::addMenu(new LLToolsSelectReflectionProbes(), "Tools.SelectReflectionProbes"); view_listener_t::addMenu(new LLToolsSelectBySurrounding(), "Tools.SelectBySurrounding"); + view_listener_t::addMenu(new LLToolsSelectionLODMode(), "Tools.SelectionLODMode"); view_listener_t::addMenu(new LLToolsShowHiddenSelection(), "Tools.ShowHiddenSelection"); view_listener_t::addMenu(new LLToolsShowSelectionLightRadius(), "Tools.ShowSelectionLightRadius"); view_listener_t::addMenu(new LLToolsEditLinkedParts(), "Tools.EditLinkedParts"); @@ -9739,6 +9830,7 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsEnablePathfindingView(), "Tools.EnablePathfindingView"); view_listener_t::addMenu(new LLToolsDoPathfindingRebakeRegion(), "Tools.DoPathfindingRebakeRegion"); view_listener_t::addMenu(new LLToolsEnablePathfindingRebakeRegion(), "Tools.EnablePathfindingRebakeRegion"); + view_listener_t::addMenu(new LLToolsCheckSelectionLODMode(), "Tools.ToolsCheckSelectionLODMode"); // Help menu // most items use the ShowFloater method diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 68c3dbc126..522c7e8109 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -73,7 +73,7 @@ void handle_buy(); void handle_take(bool take_separate = false); void handle_take_copy(); void handle_look_at_selection(const LLSD& param); -void handle_zoom_to_object(const LLUUID& object_id); +bool handle_zoom_to_object(const LLUUID& object_id); void handle_object_return(); void handle_object_delete(); void handle_object_edit(); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 990296c11e..3695478061 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -479,13 +479,19 @@ const bool check_file_extension(const std::string& filename, LLFilePicker::ELoad return true; } -const void upload_single_file(const std::vector& filenames, LLFilePicker::ELoadFilter type) +void upload_single_file( + const std::vector& filenames, + LLFilePicker::ELoadFilter type, + const LLUUID& dest) { std::string filename = filenames[0]; if (!check_file_extension(filename, type)) return; if (!filename.empty()) { + LLSD args; + args["filename"] = filename; + args["dest"] = dest; if (type == LLFilePicker::FFLOAD_WAV) { // pre-qualify wavs to make sure the format is acceptable @@ -500,12 +506,12 @@ const void upload_single_file(const std::vector& filenames, LLFileP } else { - LLFloaterReg::showInstance("upload_sound", LLSD(filename)); + LLFloaterReg::showInstance("upload_sound", args); } } if (type == LLFilePicker::FFLOAD_IMAGE) { - LLFloaterReg::showInstance("upload_image", LLSD(filename)); + LLFloaterReg::showInstance("upload_image", args); } if (type == LLFilePicker::FFLOAD_ANIM) { @@ -513,11 +519,11 @@ const void upload_single_file(const std::vector& filenames, LLFileP LLStringUtil::toLower(filename_lc); if (filename_lc.rfind(".anim") != std::string::npos) { - LLFloaterReg::showInstance("upload_anim_anim", LLSD(filename)); + LLFloaterReg::showInstance("upload_anim_anim", args); } else { - LLFloaterReg::showInstance("upload_anim_bvh", LLSD(filename)); + LLFloaterReg::showInstance("upload_anim_bvh", args); } } } @@ -818,7 +824,7 @@ bool get_bulk_upload_expected_cost( return file_count > 0; } -const void upload_bulk(const std::vector& filtered_filenames, bool allow_2k) +void upload_bulk(const std::vector& filtered_filenames, bool allow_2k, const LLUUID& dest) { S32 expected_upload_cost; S32 expected_upload_count; @@ -830,6 +836,7 @@ const void upload_bulk(const std::vector& filtered_filenames, bool key["upload_cost"] = expected_upload_cost; key["upload_count"] = expected_upload_count; key["has_2k_textures"] = (textures_2k_count > 0); + key["dest"] = dest; LLSD array; for (const std::string& str : filtered_filenames) @@ -863,7 +870,7 @@ const void upload_bulk(const std::vector& filtered_filenames, bool } -const void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type, bool allow_2k) +void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type, bool allow_2k, const LLUUID& dest) { // TODO: // Check user balance for entire cost @@ -885,7 +892,7 @@ const void upload_bulk(const std::vector& filenames, LLFilePicker:: filtered_filenames.push_back(filename); } } - upload_bulk(filtered_filenames, allow_2k); + upload_bulk(filtered_filenames, allow_2k, dest); } class LLFileUploadImage : public view_listener_t @@ -896,7 +903,7 @@ class LLFileUploadImage : public view_listener_t { gAgentCamera.changeCameraToDefault(); } - LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2), LLFilePicker::FFLOAD_IMAGE, false); + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, LLUUID::null), LLFilePicker::FFLOAD_IMAGE, false); return true; } }; @@ -927,7 +934,7 @@ class LLFileUploadSound : public view_listener_t { gAgentCamera.changeCameraToDefault(); } - LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2), LLFilePicker::FFLOAD_WAV, false); + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, LLUUID::null), LLFilePicker::FFLOAD_WAV, false); return true; } }; @@ -940,7 +947,7 @@ class LLFileUploadAnim : public view_listener_t { gAgentCamera.changeCameraToDefault(); } - LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2), LLFilePicker::FFLOAD_ANIM, false); + LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, LLUUID::null), LLFilePicker::FFLOAD_ANIM, false); return true; } }; @@ -953,7 +960,7 @@ class LLFileUploadBulk : public view_listener_t { gAgentCamera.changeCameraToDefault(); } - LLFilePickerReplyThread::startPicker(boost::bind(&upload_bulk, _1, _2, true), LLFilePicker::FFLOAD_ALL, true); + LLFilePickerReplyThread::startPicker(boost::bind(&upload_bulk, _1, _2, true, LLUUID::null), LLFilePicker::FFLOAD_ALL, true); return true; } }; @@ -1243,7 +1250,7 @@ LLUUID upload_new_resource( name, desc, compression_info, destination_folder_type, inv_type, next_owner_perms, group_perms, everyone_perms, - expected_upload_cost, show_inventory)); + expected_upload_cost, LLUUID::null, show_inventory)); upload_new_resource(uploadInfo, callback, userdata); return LLUUID::null; diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h index d99f9dc4c6..8f7df48a2e 100644 --- a/indra/newview/llviewermenufile.h +++ b/indra/newview/llviewermenufile.h @@ -74,6 +74,17 @@ bool get_bulk_upload_expected_cost( void do_bulk_upload(std::vector filenames, bool allow_2k); +void upload_single_file( + const std::vector& filenames, + LLFilePicker::ELoadFilter type, + const LLUUID& dest); + +void upload_bulk( + const std::vector& filenames, + LLFilePicker::ELoadFilter type, + bool allow_2k, + const LLUUID& dest); + //consider moving all file pickers below to more suitable place class LLFilePickerThread : public LLThread { //multi-threaded file picker (runs system specific file picker in background and calls "notify" from main thread) diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 16ddc2f89c..f11fa09ce9 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -450,7 +450,7 @@ std::string LLGridManager::getGridLabel(const std::string& grid) { std::string grid_label; std::string grid_name = getGrid(grid); - if (!grid.empty()) + if (!grid_name.empty()) { grid_label = mGridList[grid_name][GRID_LABEL_VALUE].asString(); } @@ -466,7 +466,7 @@ std::string LLGridManager::getGridId(const std::string& grid) { std::string grid_id; std::string grid_name = getGrid(grid); - if (!grid.empty()) + if (!grid_name.empty()) { grid_id = mGridList[grid_name][GRID_ID_VALUE].asString(); } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 8d90187e91..9e77b40a45 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4184,8 +4184,11 @@ void LLViewerObject::boostTexturePriority(bool boost_children /* = true */) if (isSculpted() && !isMesh()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); - LLUUID sculpt_id = sculpt_params->getSculptTexture(); - LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); + if (sculpt_params) + { + LLUUID sculpt_id = sculpt_params->getSculptTexture(); + LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); + } } if (boost_children) diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index a4f308bbf9..d39d466205 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -234,6 +234,8 @@ LLTrace::SampleStatHandle FRAMETIME_JITTER_EVENTS("frametimeevents", "Numbe FRAMETIME_JITTER_EVENTS_LAST_MINUTE("frametimeeventslastmin", "Number of frametime events in the last minute."); LLTrace::SampleStatHandle NOTRMALIZED_FRAMETIME_JITTER_SESSION("normalizedframetimejitter", "Normalized frametime jitter over the session."); +LLTrace::SampleStatHandle NFTV("nftv", "Normalized frametime variation."); +LLTrace::SampleStatHandle NORMALIZED_FRAMTIME_JITTER_PERIOD("normalizedframetimejitterperiod", "Normalized frametime jitter over the last 5 seconds."); LLTrace::EventStatHandle > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections"); @@ -322,6 +324,8 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff) sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter); sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime); + mLastNoramlizedSessionJitter = mTotalFrametimeJitter / mTotalTime; + static LLCachedControl frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f); if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold()) @@ -360,6 +364,27 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff) sample(LLStatViewer::FRAMETIME_JITTER_99TH, ninety_ninth_percentile); sample(LLStatViewer::FRAMETIME_JITTER_95TH, ninety_fifth_percentile); + F64 averageFrameTime = 0; + for (const auto& frame_time : mFrameTimes) + { + averageFrameTime += frame_time.value(); + } + averageFrameTime /= mFrameTimes.size(); + + sample(LLStatViewer::NFTV, frame_time_stddev / averageFrameTime); + mLastNormalizedFrametimeVariance = frame_time_stddev / averageFrameTime; + + // Add up all of the jitter values. + F64 totalJitter = 0; + for (const auto& frame_jitter : mFrameTimesJitter) + { + totalJitter += frame_jitter.value(); + } + + mLastNormalizedPeriodJitter = totalJitter / mLastFrameTimeSample; + + sample(LLStatViewer::NORMALIZED_FRAMTIME_JITTER_PERIOD, mLastNormalizedPeriodJitter); + mFrameTimes.clear(); mFrameTimesJitter.clear(); mLastFrameTimeSample = F64Seconds(0); @@ -648,6 +673,11 @@ void send_viewer_stats(bool include_preferences) // send fps only for time app spends in foreground agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); + + agent["normalized_session_jitter"] = LLViewerStats::instance().getLastNormalizedSessionJitter(); + agent["normalized_frametime_variance"] = LLViewerStats::instance().getLastNormalizedFrametimeVariance(); + agent["normalized_period_jitter"] = LLViewerStats::instance().getLastNormalizedPeriodJitter(); + agent["version"] = LLVersionInfo::instance().getChannelAndVersion(); std::string language = LLUI::getLanguage(); agent["language"] = language; diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 63fb7d4a17..011269d7ee 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -271,6 +271,10 @@ public: LLTrace::Recording& getRecording() { return mRecording; } const LLTrace::Recording& getRecording() const { return mRecording; } + F64 getLastNormalizedSessionJitter() const { return mLastNoramlizedSessionJitter; } + F64 getLastNormalizedFrametimeVariance() const { return mLastNormalizedFrametimeVariance; } + F64 getLastNormalizedPeriodJitter() const { return mLastNormalizedPeriodJitter; } + private: LLTrace::Recording mRecording; @@ -286,6 +290,11 @@ private: F64Seconds mTimeSinceLastEventSample; std::vector mFrameTimes; // used for frame time stats std::vector mFrameTimesJitter; // used for frame time jitter stats + + F64 mLastNoramlizedSessionJitter; // used for frame time jitter stats + F64 mLastNormalizedFrametimeVariance; // Used when submitting jitter stats + F64 mLastNormalizedPeriodJitter; + }; static const F32 SEND_STATS_PERIOD = 300.0f; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 4a9dd1c1b6..52939dbbae 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -524,7 +524,6 @@ void LLViewerTexture::updateClass() bool is_low = is_sys_low || over_pct > 0.f; static bool was_low = false; - static bool was_sys_low = false; if (is_low && !was_low) { @@ -542,7 +541,6 @@ void LLViewerTexture::updateClass() } was_low = is_low; - was_sys_low = is_sys_low; if (is_low) { @@ -1107,6 +1105,7 @@ void LLViewerFetchedTexture::init(bool firstinit) mOrigHeight = 0; mHasAux = false; mNeedsAux = false; + mLastWorkerDiscardLevel = -1; mRequestedDiscardLevel = -1; mRequestedDownloadPriority = 0.f; mFullyLoaded = false; @@ -1259,12 +1258,11 @@ void LLViewerFetchedTexture::loadFromFastCache() if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL) { - S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS; - S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS; - if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) + if (mRawImage && (mRawImage->getWidth() > DEFAULT_THUMBNAIL_DIMENSIONS || mRawImage->getHeight() > DEFAULT_THUMBNAIL_DIMENSIONS)) { - // scale oversized icon, no need to give more work to gl - mRawImage->scale(expected_width, expected_height); + // Scale oversized thumbnail + // thumbnails aren't supposed to go over DEFAULT_THUMBNAIL_DIMENSIONS + mRawImage->scale(DEFAULT_THUMBNAIL_DIMENSIONS, DEFAULT_THUMBNAIL_DIMENSIONS); } } @@ -1957,9 +1955,9 @@ bool LLViewerFetchedTexture::processFetchResults(S32& desired_discard, S32 curre bool LLViewerFetchedTexture::updateFetch() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled", false); + static LLCachedControl textures_decode_disabled(gSavedSettings, "TextureDecodeDisabled", false); - if(textures_decode_disabled) // don't fetch the surface textures in wireframe mode + if (textures_decode_disabled) // don't fetch the surface textures in wireframe mode { return false; } @@ -1994,7 +1992,7 @@ bool LLViewerFetchedTexture::updateFetch() LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - callback pending"); return false; // process any raw image data in callbacks before replacing } - if(mInFastCacheList) + if (mInFastCacheList) { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - in fast cache"); return false; @@ -2019,7 +2017,7 @@ bool LLViewerFetchedTexture::updateFetch() if (mAuxRawImage.notNull()) sAuxCount--; // keep in mind that fetcher still might need raw image, don't modify original bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mFetchState, mRawImage, mAuxRawImage, - mLastHttpGetStatus); + mLastHttpGetStatus); if (mRawImage.notNull()) sRawCount++; if (mAuxRawImage.notNull()) { @@ -2035,7 +2033,7 @@ bool LLViewerFetchedTexture::updateFetch() else { mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, - mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); } if (!processFetchResults(desired_discard, current_discard, fetch_discard, decode_priority)) @@ -2046,7 +2044,7 @@ bool LLViewerFetchedTexture::updateFetch() if (mIsFetching) { static const F32 MAX_HOLD_TIME = 5.0f; //seconds to wait before canceling fecthing if decode_priority is 0.f. - if(decode_priority > 0.0f || mStopFetchingTimer.getElapsedTimeF32() > MAX_HOLD_TIME) + if (decode_priority > 0.0f || mStopFetchingTimer.getElapsedTimeF32() > MAX_HOLD_TIME) { mStopFetchingTimer.reset(); LLAppViewer::getTextureFetch()->updateRequestPriority(mID, decode_priority); @@ -2062,7 +2060,7 @@ bool LLViewerFetchedTexture::updateFetch() LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - priority <= 0"); make_request = false; } - else if(mDesiredDiscardLevel > getMaxDiscardLevel()) + else if (mDesiredDiscardLevel > getMaxDiscardLevel()) { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > max"); make_request = false; @@ -2103,7 +2101,7 @@ bool LLViewerFetchedTexture::updateFetch() if (make_request) { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - make request"); - S32 w=0, h=0, c=0; + S32 w = 0, h = 0, c = 0; if (getDiscardLevel() >= 0) { w = mGLTexturep->getWidth(0); @@ -2121,18 +2119,19 @@ bool LLViewerFetchedTexture::updateFetch() S32 fetch_request_response = -1; S32 worker_discard = -1; fetch_request_response = LLAppViewer::getTextureFetch()->createRequest(mFTType, mUrl, getID(), getTargetHost(), decode_priority, - w, h, c, desired_discard, needsAux(), mCanUseHTTP); + w, h, c, desired_discard, needsAux(), mCanUseHTTP); if (fetch_request_response >= 0) // positive values and 0 are discard values { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - request created"); mHasFetcher = true; mIsFetching = true; + mLastWorkerDiscardLevel = worker_discard; // in some cases createRequest can modify discard, as an example // bake textures are always at discard 0 mRequestedDiscardLevel = llmin(desired_discard, fetch_request_response); mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, - mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); } else if (fetch_request_response == LLTextureFetch::CREATE_REQUEST_ERROR_TRANSITION) { @@ -2146,7 +2145,7 @@ bool LLViewerFetchedTexture::updateFetch() S32 decoded_discard; bool decoded; S32 fetch_state = LLAppViewer::getTextureFetch()->getLastFetchState(mID, desired_discard, decoded_discard, decoded); - if (fetch_state > 1 && decoded && decoded_discard >=0 && decoded_discard <= desired_discard) + if (fetch_state > 1 && decoded && decoded_discard >= 0 && decoded_discard <= desired_discard) { // worker actually has the image if (mRawImage.notNull()) sRawCount--; @@ -2770,11 +2769,9 @@ void LLViewerFetchedTexture::saveRawImage() } else if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL) { - S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS; - S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS; - if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height) + if (mRawImage->getWidth() > DEFAULT_THUMBNAIL_DIMENSIONS || mRawImage->getHeight() > DEFAULT_THUMBNAIL_DIMENSIONS) { - mSavedRawImage = new LLImageRaw(expected_width, expected_height, mRawImage->getComponents()); + mSavedRawImage = new LLImageRaw(DEFAULT_THUMBNAIL_DIMENSIONS, DEFAULT_THUMBNAIL_DIMENSIONS, mRawImage->getComponents()); mSavedRawImage->copyScaled(mRawImage); } else diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index e1582c74bd..f9311d85cb 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -444,6 +444,7 @@ protected: bool mKnownDrawSizeChanged ; std::string mUrl; + S32 mLastWorkerDiscardLevel; S32 mRequestedDiscardLevel; F32 mRequestedDownloadPriority; S32 mFetchState; diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp index 8d935e4243..3ccfbea6e2 100644 --- a/indra/newview/llviewerthrottle.cpp +++ b/indra/newview/llviewerthrottle.cpp @@ -225,7 +225,7 @@ void LLViewerThrottle::setMaxBandwidth(F32 kbits_per_second, bool from_event) void LLViewerThrottle::load() { - mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS")*1024; + mMaxBandwidth = getMaxBandwidthKbps() * 1024; resetDynamicThrottle(); mCurrent.dump(); } @@ -242,6 +242,15 @@ void LLViewerThrottle::sendToSim() const mCurrent.sendToSim(); } +F32 LLViewerThrottle::getMaxBandwidthKbps() +{ + constexpr F32 MIN_BANDWIDTH = 100.0f; // 100 Kbps + constexpr F32 MAX_BANDWIDTH = 10000.0f; // 10 Mbps + + static LLCachedControl bandwidth(gSavedSettings, "ThrottleBandwidthKBPS", 3000.0); + return llclamp(bandwidth(), MIN_BANDWIDTH, MAX_BANDWIDTH); +} + LLViewerThrottleGroup LLViewerThrottle::getThrottleGroup(const F32 bandwidth_kbps) { diff --git a/indra/newview/llviewerthrottle.h b/indra/newview/llviewerthrottle.h index 9973c88549..ef898a97d7 100644 --- a/indra/newview/llviewerthrottle.h +++ b/indra/newview/llviewerthrottle.h @@ -64,6 +64,7 @@ public: void save() const; void sendToSim() const; + static F32 getMaxBandwidthKbps(); F32 getMaxBandwidth()const { return mMaxBandwidth; } F32 getCurrentBandwidth() const { return mCurrentBandwidth; } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 6aade15d23..0edb7c70ea 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1424,11 +1424,17 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask mWindow->showCursorFromMouseMove(); - if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME - && !gDisconnected) + if (!gDisconnected) + { + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } + } } void LLViewerWindow::handleMouseDragged(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1547,6 +1553,10 @@ bool LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, bool repeated) { gAgent.clearAFK(); } + else + { + gAwayTriggerTimer.reset(); + } // *NOTE: We want to interpret KEY_RETURN later when it arrives as // a Unicode char, not as a keydown. Otherwise when client frame @@ -2299,6 +2309,18 @@ void LLViewerWindow::initWorldUI() url = LLWeb::expandURLSubstitutions(url, LLSD()); avatar_welcome_pack->navigateTo(url, HTTP_CONTENT_TEXT_HTML); } + LLMediaCtrl* search = LLFloaterReg::getInstance("search")->findChild("search_contents"); + if (search) + { + search->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + } + LLMediaCtrl* marketplace = LLFloaterReg::getInstance("marketplace")->getChild("marketplace_contents"); + if (marketplace) + { + marketplace->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); + std::string url = gSavedSettings.getString("MarketplaceURL"); + marketplace->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + } } } @@ -3015,7 +3037,8 @@ bool LLViewerWindow::handleKey(KEY key, MASK mask) { if ((focusedFloaterName == "nearby_chat") || (focusedFloaterName == "im_container") || (focusedFloaterName == "impanel")) { - if (gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) + LLCachedControl key_move(gSavedSettings, "ArrowKeysAlwaysMove"); + if (key_move()) { // let Control-Up and Control-Down through for chat line history, if (!(key == KEY_UP && mask == MASK_CONTROL) @@ -3029,10 +3052,9 @@ bool LLViewerWindow::handleKey(KEY key, MASK mask) case KEY_RIGHT: case KEY_UP: case KEY_DOWN: - case KEY_PAGE_UP: - case KEY_PAGE_DOWN: - case KEY_HOME: - case KEY_END: + case KEY_PAGE_UP: //jump + case KEY_PAGE_DOWN: // down + case KEY_HOME: // toggle fly // when chatbar is empty or ArrowKeysAlwaysMove set, // pass arrow keys on to avatar... return false; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index dd59979a6c..c6a7a59034 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -575,7 +575,7 @@ private: // joint states to be animated //------------------------------------------------------------------------- LLPointer mPelvisState; - LLCharacter* mCharacter; + LLCharacter* mCharacter; }; /** @@ -684,6 +684,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mPreviousFullyLoaded(false), mFullyLoadedInitialized(false), mLastCloudAttachmentCount(-1), + mFullyLoadedFrameCounter(0), mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mLoadedCallbacksPaused(false), mLoadedCallbackTextures(0), @@ -777,11 +778,9 @@ std::string LLVOAvatar::avString() const { return " " + getFullname() + " "; } - else - { - std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); - return " Avatar '" + getFullname() + "' " + viz_string + " "; - } + + std::string status = LLVOAvatar::rezStatusToString(getRezzedStatus()); + return " Avatar '" + getDebugName() + "' " + status + " "; } void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment) @@ -804,10 +803,10 @@ void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string c if (gSavedSettings.getBOOL("DebugAvatarRezTime")) { LLSD args; - args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); - args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); + args["EXISTENCE"] = llformat("%d", (U32)mDebugExistenceTimer.getElapsedTimeF32()); + args["TIME"] = llformat("%d", (U32)mRuthDebugTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); - LLNotificationsUtil::add(notification_name,args); + LLNotificationsUtil::add(notification_name, args); } } @@ -820,14 +819,14 @@ LLVOAvatar::~LLVOAvatar() if (!mFullyLoaded) { - debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud"); + debugAvatarRezTime("AvatarRezLeftCloudNotification", "left after ruth seconds as cloud"); } else { - debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding"); + debugAvatarRezTime("AvatarRezLeftNotification", "left sometime after declouding"); } - if(mTuned) + if (mTuned) { LLPerfStats::tunedAvatars--; mTuned = false; @@ -969,7 +968,7 @@ bool LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars) ++grey_avatars; } } - return !grey_avatars; + return grey_avatars == 0; } // static @@ -2983,7 +2982,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (LLVOAvatar::sJointDebug) { - LL_INFOS() << getFullname() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << LL_ENDL; + LL_INFOS() << getDebugName() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << LL_ENDL; } LLJoint::sNumUpdates = 0; @@ -3203,17 +3202,17 @@ F32 LLVOAvatar::calcMorphAmount() const void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) { // Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync - if ( voice_enabled + if (voice_enabled && mLastRezzedStatus > 0 // no point updating lip-sync for clouds && LLVoiceVisualizer::getLipSyncEnabled() - && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) + && LLVoiceClient::getInstance()->getIsSpeaking(mID)) { F32 ooh_morph_amount = 0.0f; F32 aah_morph_amount = 0.0f; mVoiceVisualizer->lipSyncOohAah( ooh_morph_amount, aah_morph_amount ); - if( mOohMorph ) + if (mOohMorph) { F32 ooh_weight = mOohMorph->getMinWeight() + ooh_morph_amount * (mOohMorph->getMaxWeight() - mOohMorph->getMinWeight()); @@ -3221,7 +3220,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) mOohMorph->setWeight( ooh_weight); } - if( mAahMorph ) + if (mAahMorph) { F32 aah_weight = mAahMorph->getMinWeight() + aah_morph_amount * (mAahMorph->getMaxWeight() - mAahMorph->getMinWeight()); @@ -4308,8 +4307,8 @@ void LLVOAvatar::computeUpdatePeriod() { const LLVector4a* ext = mDrawable->getSpatialExtents(); LLVector4a size; - size.setSub(ext[1],ext[0]); - F32 mag = size.getLength3().getF32()*0.5f; + size.setSub(ext[1], ext[0]); + F32 mag = size.getLength3().getF32() * 0.5f; const S32 UPDATE_RATE_SLOW = 64; const S32 UPDATE_RATE_MED = 48; @@ -4319,14 +4318,14 @@ void LLVOAvatar::computeUpdatePeriod() { // visually muted avatars update at lowest rate mUpdatePeriod = UPDATE_RATE_SLOW; } - else if (! shouldImpostor() - || mDrawable->mDistanceWRTCamera < 1.f + mag) + else if (!shouldImpostor() + || mDrawable->mDistanceWRTCamera < 1.f + mag) { // first 25% of max visible avatars are not impostored // also, don't impostor avatars whose bounding box may be penetrating the // impostor camera near clip plane mUpdatePeriod = 1; } - else if ( shouldImpostor(4.0) ) + else if (shouldImpostor(4.0)) { //background avatars are REALLY slow updating impostors mUpdatePeriod = UPDATE_RATE_SLOW; } @@ -4335,7 +4334,7 @@ void LLVOAvatar::computeUpdatePeriod() // Don't update cloud avatars too often mUpdatePeriod = UPDATE_RATE_SLOW; } - else if ( shouldImpostor(3.0) ) + else if (shouldImpostor(3.0)) { //back 25% of max visible avatars are slow updating impostors mUpdatePeriod = UPDATE_RATE_MED; } @@ -4766,14 +4765,6 @@ bool LLVOAvatar::updateCharacter(LLAgent &agent) } bool visible = isVisible(); - bool is_control_avatar = isControlAvatar(); // capture state to simplify tracing - bool is_attachment = false; - - if (is_control_avatar) - { - LLControlAvatar *cav = dynamic_cast(this); - is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects - } // For fading out the names above heads, only let the timer // run if we're visible. @@ -6184,8 +6175,11 @@ void LLVOAvatar::resetAnimations() flushAllMotions(); } -// Override selectively based on avatar sex and whether we're using new -// animations. +//----------------------------------------------------------------------------- +// remapMotionID() +// Override selectively based on avatar sex and whether we're using new animations. +//----------------------------------------------------------------------------- +// virtual LLUUID LLVOAvatar::remapMotionID(const LLUUID& id) { static LLCachedControl use_new_walk_run(gSavedSettings, "UseNewWalkRun"); @@ -6235,7 +6229,6 @@ LLUUID LLVOAvatar::remapMotionID(const LLUUID& id) } return result; - } //----------------------------------------------------------------------------- @@ -6243,6 +6236,7 @@ LLUUID LLVOAvatar::remapMotionID(const LLUUID& id) // id is the asset if of the animation to start // time_offset is the offset into the animation at which to start playing //----------------------------------------------------------------------------- +// virtual bool LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset) { LL_DEBUGS("Motion") << "motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << LL_ENDL; @@ -6265,6 +6259,7 @@ bool LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset) //----------------------------------------------------------------------------- // stopMotion() //----------------------------------------------------------------------------- +// virtual bool LLVOAvatar::stopMotion(const LLUUID& id, bool stop_immediate) { LL_DEBUGS("Motion") << "Motion requested " << id.asString() << " " << gAnimLibrary.animationName(id) << LL_ENDL; @@ -6304,15 +6299,30 @@ void LLVOAvatar::stopMotionFromSource(const LLUUID& source_id) //----------------------------------------------------------------------------- // addDebugText() //----------------------------------------------------------------------------- +// virtual void LLVOAvatar::addDebugText(const std::string& text) { mDebugText.append(1, '\n'); mDebugText.append(text); } +//----------------------------------------------------------------------------- +// getDebugName() +//----------------------------------------------------------------------------- +// virtual +std::string LLVOAvatar::getDebugName() const +{ +#if LL_RELEASE_WITH_DEBUG_INFO + return getFullname(); +#else + return getID().asString(); +#endif // LL_RELEASE_WITH_DEBUG_INFO +} + //----------------------------------------------------------------------------- // getID() //----------------------------------------------------------------------------- +// virtual const LLUUID& LLVOAvatar::getID() const { return mID; @@ -6322,6 +6332,7 @@ const LLUUID& LLVOAvatar::getID() const // getJoint() //----------------------------------------------------------------------------- // RN: avatar joints are multi-rooted to include screen-based attachments +// virtual LLJoint* LLVOAvatar::getJoint(std::string_view name) { joint_map_t::iterator iter = mJointMap.find(name); @@ -6447,6 +6458,7 @@ bool LLVOAvatar::jointIsRiggedTo(const LLJoint *joint) const void LLVOAvatar::clearAttachmentOverrides() { + for (S32 i=0; i meshes_seen; @@ -6600,11 +6614,11 @@ void LLVOAvatar::updateAttachmentOverrides() } } pelvis_fixups = mPelvisFixups; - //dumpArchetypeXML(getFullname() + "_paranoid_updated"); + //dumpArchetypeXML(getDebugName() + "_paranoid_updated"); // Rebuild and compare rebuildAttachmentOverrides(); - //dumpArchetypeXML(getFullname() + "_paranoid_rebuilt"); + //dumpArchetypeXML(getDebugName() + "_paranoid_rebuilt"); bool mismatched = false; for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) { @@ -6833,22 +6847,22 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const { std::stringstream ss; std::copy(pos_names.begin(), pos_names.end(), std::ostream_iterator(ss, ",")); - LL_INFOS() << getFullname() << " attachment positions defined for joints: " << ss.str() << "\n" << LL_ENDL; + LL_INFOS() << avString() << " attachment positions defined for joints: " << ss.str() << "\n" << LL_ENDL; } else { - LL_DEBUGS("Avatar") << getFullname() << " no attachment positions defined for any joints" << "\n" << LL_ENDL; + LL_DEBUGS("Avatar") << avString() << " no attachment positions defined for any joints" << "\n" << LL_ENDL; } if (scale_names.size()) { std::stringstream ss; std::copy(scale_names.begin(), scale_names.end(), std::ostream_iterator(ss, ",")); - LL_INFOS() << getFullname() << " attachment scales defined for joints: " << ss.str() << "\n" << LL_ENDL; + LL_INFOS() << getDebugName() << " attachment scales defined for joints: " << ss.str() << "\n" << LL_ENDL; } else { - LL_INFOS() << getFullname() << " no attachment scales defined for any joints" << "\n" << LL_ENDL; + LL_INFOS() << getDebugName() << " no attachment scales defined for any joints" << "\n" << LL_ENDL; } if (!verbose) @@ -8235,7 +8249,7 @@ bool LLVOAvatar::getHasMissingParts() const void LLVOAvatar::updateRezzedStatusTimers(S32 rez_status) { // State machine for rezzed status. Statuses are -1 on startup, 0 - // = cloud, 1 = gray, 2 = downloading, 3 = waiting for attachments, 4 = full. + // Statuses are -1 on startup, 0 = cloud, 1 = gray, 2 = downloading, 3 = waiting for attachments, 4 = full. // Purpose is to collect time data for each it takes avatar to reach // various loading landmarks: gray, textured (partial), textured fully. @@ -8255,7 +8269,7 @@ void LLVOAvatar::updateRezzedStatusTimers(S32 rez_status) if (rez_status < mLastRezzedStatus) { // load level has decreased. start phase timers for higher load levels. - for (S32 i = rez_status+1; i <= mLastRezzedStatus; i++) + for (S32 i = rez_status + 1; i <= mLastRezzedStatus; i++) { startPhase("load_" + LLVOAvatar::rezStatusToString(i)); } @@ -8263,7 +8277,7 @@ void LLVOAvatar::updateRezzedStatusTimers(S32 rez_status) else if (rez_status > mLastRezzedStatus) { // load level has increased. stop phase timers for lower and equal load levels. - for (S32 i = llmax(mLastRezzedStatus+1,1); i <= rez_status; i++) + for (S32 i = llmax(mLastRezzedStatus + 1, 1); i <= rez_status; i++) { stopPhase("load_" + LLVOAvatar::rezStatusToString(i)); stopPhase("first_load_" + LLVOAvatar::rezStatusToString(i), false); @@ -8508,19 +8522,19 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) F32 first_use_delay = FIRST_APPEARANCE_CLOUD_MIN_DELAY; if (!isSelf() && loading) { - // Note that textures can causes 60s delay on thier own - // so this delay might end up on top of textures' delay - first_use_delay = llclamp( - mFirstAppearanceMessageTimer.getElapsedTimeF32(), - FIRST_APPEARANCE_CLOUD_MIN_DELAY, - FIRST_APPEARANCE_CLOUD_MAX_DELAY); + // Note that textures can causes 60s delay on thier own + // so this delay might end up on top of textures' delay + first_use_delay = llclamp( + mFirstAppearanceMessageTimer.getElapsedTimeF32(), + FIRST_APPEARANCE_CLOUD_MIN_DELAY, + FIRST_APPEARANCE_CLOUD_MAX_DELAY); - if (shouldImpostor()) - { - // Impostors are less of a priority, - // let them stay cloud longer - first_use_delay *= FIRST_APPEARANCE_CLOUD_IMPOSTOR_MODIFIER; - } + if (shouldImpostor()) + { + // Impostors are less of a priority, + // let them stay cloud longer + first_use_delay *= FIRST_APPEARANCE_CLOUD_IMPOSTOR_MODIFIER; + } } mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > first_use_delay); } @@ -8537,12 +8551,12 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) // did our loading state "change" from last call? // FIXME runway - why are we updating every 30 calls even if nothing has changed? // This causes updateLOD() to run every 30 frames, among other things. + bool fully_loaded_changed = (mFullyLoaded != mPreviousFullyLoaded); const S32 UPDATE_RATE = 30; bool changed = ((mFullyLoaded != mPreviousFullyLoaded) || // if the value is different from the previous call - (!mFullyLoadedInitialized) || // if we've never been called before - (mFullyLoadedFrameCounter % UPDATE_RATE == 0)); // every now and then issue a change - bool fully_loaded_changed = (mFullyLoaded != mPreviousFullyLoaded); + (!mFullyLoadedInitialized) || // if we've never been called before + (mFullyLoadedFrameCounter % UPDATE_RATE == 0)); // every now and then issue a change mPreviousFullyLoaded = mFullyLoaded; mFullyLoadedInitialized = true; @@ -8560,6 +8574,7 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) mNeedsImpostorUpdate = true; mLastImpostorUpdateReason = 6; } + return changed; } @@ -9454,12 +9469,12 @@ void dump_visual_param(apr_file_t* file, LLVisualParam* viewer_param, F32 value) void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix, const LLAppearanceMessageContents& contents) { - std::string outfilename = get_sequential_numbered_file_name(dump_prefix,".xml"); + std::string outfilename = get_sequential_numbered_file_name(dump_prefix, ".xml"); const std::vector& params_for_dump = contents.mParamWeights; const LLTEContents& tec = contents.mTEContents; LLAPRFile outfile; - std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename); + std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, outfilename); outfile.open(fullpath, LL_APR_WB ); apr_file_t* file = outfile.getFileHandle(); if (!file) @@ -9672,7 +9687,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) static LLCachedControl enable_verbose_dumps(gSavedSettings, "DebugAvatarAppearanceMessage"); static LLCachedControl block_avatar_appearance_messages(gSavedSettings, "BlockAvatarAppearanceMessages"); - std::string dump_prefix = getFullname() + "_" + (isSelf()?"s":"o") + "_"; + std::string dump_prefix = getDebugName() + (isSelf() ? "_s_" : "_o_"); if (block_avatar_appearance_messages) { LL_WARNS() << "Blocking AvatarAppearance message" << LL_ENDL; @@ -10306,17 +10321,13 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara std::string outprefix(prefix); if (outprefix.empty()) { - outprefix = getFullname() + (isSelf()?"_s":"_o"); + outprefix = getDebugName() + (isSelf() ? "_s" : "_o"); } - if (outprefix.empty()) - { - outprefix = std::string("new_archetype"); - } - std::string outfilename = get_sequential_numbered_file_name(outprefix,".xml"); + std::string outfilename = get_sequential_numbered_file_name(outprefix, ".xml"); LLAPRFile outfile; LLWearableType *wr_inst = LLWearableType::getInstance(); - std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename); + std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, outfilename); if (APR_SUCCESS == outfile.open(fullpath, LL_APR_WB )) { apr_file_t* file = outfile.getFileHandle(); @@ -10802,7 +10813,7 @@ void LLVOAvatar::updateRiggingInfo() { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - LL_DEBUGS("RigSpammish") << getFullname() << " updating rig tab" << LL_ENDL; + LL_DEBUGS("RigSpammish") << getDebugName() << " updating rig tab" << LL_ENDL; // use a local static for scratch space to avoid reallocation here static std::vector volumes; @@ -11783,16 +11794,24 @@ void LLVOAvatar::readProfileQuery(S32 retries) } else - { // wait until next frame - LLUUID id = getID(); + { + // wait until next frame + const LLUUID id = getID(); - LL::WorkQueue::getInstance("mainloop")->post([id, retries] { - LLVOAvatar* avatar = (LLVOAvatar*) gObjectList.findObject(id); - if(avatar) + LL::WorkQueue::getInstance("mainloop")->post([id, retries] + { + LLViewerObject* object = gObjectList.findObject(id); + if (object + && !object->isDead() + && object->isAvatar()) // probably excessive, pcode isn't supposed to change { - avatar->readProfileQuery(retries); + LLVOAvatar* avatar = (LLVOAvatar*)object; + if (avatar) + { + avatar->readProfileQuery(retries); + } } - }); + }); } } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 178665f4c5..1e563c4869 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -233,6 +233,7 @@ public: virtual void onActiveOverrideMeshesChanged(); /*virtual*/ const LLUUID& getID() const; + /*virtual*/ std::string getDebugName() const; /*virtual*/ void addDebugText(const std::string& text); /*virtual*/ F32 getTimeDilation(); /*virtual*/ void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm); @@ -330,16 +331,16 @@ public: // avatar render cost - U32 getVisualComplexity() { return mVisualComplexity; }; + U32 getVisualComplexity() { return mVisualComplexity; }; // surface area calculation - F32 getAttachmentSurfaceArea() { return mAttachmentSurfaceArea; }; + F32 getAttachmentSurfaceArea() { return mAttachmentSurfaceArea; }; - U32 getReportedVisualComplexity() { return mReportedVisualComplexity; }; // Numbers as reported by the SL server - void setReportedVisualComplexity(U32 value) { mReportedVisualComplexity = value; }; + U32 getReportedVisualComplexity() { return mReportedVisualComplexity; }; // Numbers as reported by the SL server + void setReportedVisualComplexity(U32 value) { mReportedVisualComplexity = value; }; - S32 getUpdatePeriod() { return mUpdatePeriod; }; - const LLColor4 & getMutedAVColor() { return mMutedAVColor; }; + S32 getUpdatePeriod() { return mUpdatePeriod; }; + const LLColor4 & getMutedAVColor() { return mMutedAVColor; }; static void updateImpostorRendering(U32 newMaxNonImpostorsValue); void idleUpdateBelowWater(); @@ -403,12 +404,11 @@ public: virtual bool getHasMissingParts() const; bool isFullyTextured() const; bool hasGray() const; - S32 getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = textured, 3 = textured and fully downloaded. + S32 getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = textured, 3 = waiting for attachments, 4 = full. void updateRezzedStatusTimers(S32 status); S32 mLastRezzedStatus; - void startPhase(const std::string& phase_name); void stopPhase(const std::string& phase_name, bool err_check = true); void clearPhases(); @@ -743,7 +743,7 @@ protected: LLViewerTexLayerSet* getTexLayerSet(const U32 index) const { return dynamic_cast(mBakedTextureDatas[index].mTexLayerSet); } - LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ; + LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList; bool mLoadedCallbacksPaused; S32 mLoadedCallbackTextures; // count of 'loaded' baked textures, filled from mCallbackTextureList LLFrameTimer mLastTexCallbackAddedTime; diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 27c105c8d6..501828eee8 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -1875,11 +1875,11 @@ void LLVOCache::removeGenericExtrasForHandle(U64 handle) } // NOTE: when removing the extras, we must also remove the objects so the simulator will send us a full upddate with the valid overrides - auto* entry = mHandleEntryMap[handle]; - if (entry) + handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle); + if (iter != mHandleEntryMap.end()) { - LL_WARNS("GLTF", "VOCache") << "Removing generic extras for handle " << entry->mHandle << "Filename: " << getObjectCacheExtrasFilename(handle) << LL_ENDL; - removeEntry(entry); + LL_WARNS("GLTF", "VOCache") << "Removing generic extras for handle " << handle << "Filename: " << getObjectCacheExtrasFilename(handle) << LL_ENDL; + removeEntry(iter->second); } else { diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp index 25b0e69436..d2c947fef2 100644 --- a/indra/newview/llvoicecallhandler.cpp +++ b/indra/newview/llvoicecallhandler.cpp @@ -38,6 +38,11 @@ public: { } + virtual bool canHandleUntrusted(const LLSD ¶ms, const LLSD &query_map, LLMediaCtrl *web, const std::string &nav_type) + { + return (nav_type == NAV_TYPE_CLICKED || nav_type == NAV_TYPE_EXTERNAL); + } + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { //Make sure we have some parameters diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index cf128f381a..b3ac28eb7a 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -369,6 +369,12 @@ void LLVoiceChannel::resume() { if (sSuspendedVoiceChannel) { + if (sSuspendedVoiceChannel->callStarted()) + { + // should have channel data already, restart + sSuspendedVoiceChannel->setState(STATE_READY); + } + // won't do anything if call is already started sSuspendedVoiceChannel->activate(); } else diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 627d759df4..9667bd028f 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -52,6 +52,7 @@ #include "llcachename.h" #include "llimview.h" // for LLIMMgr #include "llworld.h" +#include "llviewerregion.h" #include "llparcel.h" #include "llviewerparcelmgr.h" #include "llfirstuse.h" @@ -2004,6 +2005,33 @@ bool LLWebRTCVoiceClient::sessionState::processConnectionStates() return !mWebRTCConnections.empty(); } +// Helper function to check if a region supports WebRTC voice +bool LLWebRTCVoiceClient::estateSessionState::isRegionWebRTCEnabled(const LLUUID& regionID) +{ + LLViewerRegion* region = LLWorld::getInstance()->getRegionFromID(regionID); + if (!region) + { + LL_WARNS("Voice") << "Could not find region " << regionID + << " for voice server type validation" << LL_ENDL; + return false; + } + + LLSD simulatorFeatures; + region->getSimulatorFeatures(simulatorFeatures); + + bool isWebRTCEnabled = simulatorFeatures.has("VoiceServerType") && + simulatorFeatures["VoiceServerType"].asString() == "webrtc"; + + if (!isWebRTCEnabled) + { + LL_DEBUGS("Voice") << "Region " << regionID << " VoiceServerType is not 'webrtc' (got: " + << (simulatorFeatures.has("VoiceServerType") ? simulatorFeatures["VoiceServerType"].asString() : "none") << ")" + << LL_ENDL; + } + + return isWebRTCEnabled; +} + // processing of spatial voice connection states requires special handling. // as neighboring regions need to be started up or shut down depending // on our location. @@ -2028,6 +2056,13 @@ bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() // shut down connections to neighbors that are too far away. spatialConnection.get()->shutDown(); } + else if (!isRegionWebRTCEnabled(regionID)) + { + // shut down connections to neighbors that no longer support WebRTC voice. + LL_DEBUGS("Voice") << "Shutting down connection to neighbor region " << regionID + << " - no longer supports WebRTC voice" << LL_ENDL; + spatialConnection.get()->shutDown(); + } if (!spatialConnection.get()->isShuttingDown()) { neighbor_ids.erase(regionID); @@ -2037,11 +2072,20 @@ bool LLWebRTCVoiceClient::estateSessionState::processConnectionStates() // add new connections for new neighbors for (auto &neighbor : neighbor_ids) { - connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); + // Only connect if the region supports WebRTC voice server type + if (isRegionWebRTCEnabled(neighbor)) + { + connectionPtr_t connection(new LLVoiceWebRTCSpatialConnection(neighbor, INVALID_PARCEL_ID, mChannelID)); - mWebRTCConnections.push_back(connection); - connection->setMuteMic(mMuted); - connection->setSpeakerVolume(mSpeakerVolume); + mWebRTCConnections.push_back(connection); + connection->setMuteMic(mMuted); // mute will be set for primary connection when that connection comes up + connection->setSpeakerVolume(mSpeakerVolume); + } + else + { + LL_DEBUGS("Voice") << "Skipping neighbor region " << neighbor + << " - does not support WebRTC voice" << LL_ENDL; + } } } return LLWebRTCVoiceClient::sessionState::processConnectionStates(); @@ -2264,7 +2308,6 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) return; } - bool iceCompleted = false; LLSD body; if (!connection->mIceCandidates.empty() || connection->mIceCompleted) { @@ -2303,7 +2346,6 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) LLSD body_candidate; body_candidate["completed"] = true; body["candidate"] = body_candidate; - iceCompleted = connection->mIceCompleted; connection->mIceCompleted = false; } @@ -2391,6 +2433,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac } LL_DEBUGS("Voice") << "On AudioEstablished." << LL_ENDL; mWebRTCAudioInterface = audio_interface; + mWebRTCAudioInterface->setMute(true); // mute will be set appropriately later when we finish setting up. setVoiceConnectionState(VOICE_STATE_SESSION_ESTABLISHED); }); } @@ -2750,7 +2793,12 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() } // update the peer connection with the various characteristics of // this connection. - mWebRTCAudioInterface->setMute(mMuted); + // For spatial this connection will come up as muted, but will be set to the appropriate + // value later on when we determine the regions we connect to. + if (!isSpatial()) + { + mWebRTCAudioInterface->setMute(mMuted); + } mWebRTCAudioInterface->setReceiveVolume(mSpeakerVolume); LLWebRTCVoiceClient::getInstance()->OnConnectionEstablished(mChannelID, mRegionID); setVoiceConnectionState(VOICE_STATE_WAIT_FOR_DATA_CHANNEL); @@ -2795,6 +2843,10 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() if (primary != mPrimary) { mPrimary = primary; + if (mWebRTCAudioInterface) + { + mWebRTCAudioInterface->setMute(mMuted || !mPrimary); + } sendJoin(); } } @@ -2926,7 +2978,6 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b return; } boost::json::object voice_data = voice_data_parsed.as_object(); - bool new_participant = false; boost::json::object mute; boost::json::object user_gain; for (auto &participant_elem : voice_data) @@ -2979,7 +3030,6 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b } } - new_participant |= joined; if (!participant && joined && (primary || !isSpatial())) { participant = LLWebRTCVoiceClient::getInstance()->addParticipantByID(mChannelID, agent_id, mRegionID); @@ -3097,10 +3147,7 @@ LLVoiceWebRTCSpatialConnection::LLVoiceWebRTCSpatialConnection(const LLUUID ® LLVoiceWebRTCConnection(regionID, channelID), mParcelLocalID(parcelLocalID) { - if (gAgent.getRegion()) - { - mPrimary = (regionID == gAgent.getRegion()->getRegionID()); - } + mPrimary = false; // will be set to primary after connection established } LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection() diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ff82d2739d..71347f206a 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -351,6 +351,9 @@ public: bool isSpatial() override { return true; } bool isEstate() override { return true; } bool isCallbackPossible() override { return false; } + + private: + bool isRegionWebRTCEnabled(const LLUUID& regionID); }; class parcelSessionState : public sessionState diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index b96df0c79f..80fc486a10 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -352,8 +352,11 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, if (isSculpted()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); - sculpt_id = sculpt_params->getSculptTexture(); - sculpt_type = sculpt_params->getSculptType(); + if (sculpt_params) + { + sculpt_id = sculpt_params->getSculptTexture(); + sculpt_type = sculpt_params->getSculptType(); + } LL_DEBUGS("ObjectUpdate") << "uuid " << mID << " set sculpt_id " << sculpt_id << LL_ENDL; } @@ -1191,12 +1194,15 @@ void LLVOVolume::updateSculptTexture() if (isSculpted() && !isMesh()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); - LLUUID id = sculpt_params->getSculptTexture(); - if (id.notNull()) + if (sculpt_params) { - mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_SCULPTED, LLViewerTexture::LOD_TEXTURE); - mSculptTexture->forceToSaveRawImage(0, F32_MAX); - mSculptTexture->setKnownDrawSize(256, 256); + LLUUID id = sculpt_params->getSculptTexture(); + if (id.notNull()) + { + mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_SCULPTED, LLViewerTexture::LOD_TEXTURE); + mSculptTexture->forceToSaveRawImage(0, F32_MAX); + mSculptTexture->setKnownDrawSize(256, 256); + } } mSkinInfoUnavaliable = false; @@ -1484,6 +1490,7 @@ bool LLVOVolume::calcLOD() const LLVector3* box = avatar->getLastAnimExtents(); LLVector3 diag = box[1] - box[0]; radius = diag.magVec() * 0.5f; + LL_DEBUGS("DynamicBox") << avatar->getDebugName() << " diag " << diag << " radius " << radius << LL_ENDL; } else { @@ -1494,6 +1501,7 @@ bool LLVOVolume::calcLOD() const LLVector3* box = avatar->getLastAnimExtents(); LLVector3 diag = box[1] - box[0]; radius = diag.magVec(); // preserve old BinRadius behavior - 2x off + LL_DEBUGS("DynamicBox") << avatar->getDebugName() << " diag " << diag << " radius " << radius << LL_ENDL; } if (distance <= 0.f || radius <= 0.f) { @@ -1558,11 +1566,16 @@ bool LLVOVolume::calcLOD() mLODAdjustedDistance = distance; + static LLCachedControl debug_selection_lods(gSavedSettings, "DebugSelectionLODs", 0); if (isHUDAttachment()) { // HUDs always show at highest detail cur_detail = 3; } + else if (isSelected() && debug_selection_lods() >= 0) + { + cur_detail = llmin(debug_selection_lods(), 3); + } else { cur_detail = computeLODDetail(ll_round(distance, 0.01f), ll_round(radius, 0.01f), lod_factor); @@ -3587,12 +3600,15 @@ bool LLVOVolume::isMesh() const if (isSculpted()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); - U8 sculpt_type = sculpt_params->getSculptType(); - - if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) - // mesh is a mesh + if (sculpt_params) { - return true; + U8 sculpt_type = sculpt_params->getSculptType(); + + if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + // mesh is a mesh + { + return true; + } } } diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index c708e804b2..5fb22184c3 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -185,6 +185,7 @@ LLPanelWearableOutfitItem::LLPanelWearableOutfitItem(LLViewerInventoryItem* item // virtual void LLPanelWearableOutfitItem::updateItem(const std::string& name, + bool favorite, EItemState item_state) { std::string search_label = name; @@ -215,7 +216,7 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name, } } - LLPanelInventoryListItemBase::updateItem(search_label, item_state); + LLPanelInventoryListItemBase::updateItem(search_label, favorite, item_state); } ////////////////////////////////////////////////////////////////////////// @@ -442,6 +443,7 @@ LLPanelAttachmentListItem* LLPanelAttachmentListItem::create(LLViewerInventoryIt } void LLPanelAttachmentListItem::updateItem(const std::string& name, + bool favorite, EItemState item_state) { std::string title_joint = name; @@ -459,7 +461,7 @@ void LLPanelAttachmentListItem::updateItem(const std::string& name, title_joint = title_joint + " (" + trans_name + ")"; } - LLPanelInventoryListItemBase::updateItem(title_joint, item_state); + LLPanelInventoryListItemBase::updateItem(title_joint, favorite, item_state); } ////////////////////////////////////////////////////////////////////////// @@ -487,7 +489,7 @@ bool LLPanelDummyClothingListItem::postBuild() addWidgetToRightSide("btn_add_panel"); setIconImage(LLInventoryIcon::getIcon(LLAssetType::AT_CLOTHING, LLInventoryType::IT_NONE, mWearableType, false)); - updateItem(wearableTypeToString(mWearableType)); + updateItem(wearableTypeToString(mWearableType), false); // Make it look loke clothing item - reserve space for 'delete' button setLeftWidgetsWidth(getChildView("item_icon")->getRect().mLeft); @@ -921,8 +923,8 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id)); registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); registrar.add("Wearable.TakeOffDetach", - boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); + boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); // Register handlers for clothing. registrar.add("Clothing.TakeOff", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); @@ -932,6 +934,7 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers for attachments. registrar.add("Attachment.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); + registrar.add("Attachment.Favorite", boost::bind(toggle_favorites, ids)); registrar.add("Attachment.Touch", boost::bind(handle_attachment_touch, selected_id)); registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2)); @@ -965,6 +968,8 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu U32 n_touchable = 0; // number of touchable items among the selected ones bool can_be_worn = true; + bool can_favorite = false; + bool can_unfavorite = false; for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) { @@ -986,6 +991,12 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu const bool is_editable = get_is_item_editable(id); const bool is_touchable = enable_attachment_touch(id); const bool is_already_worn = gAgentWearables.selfHasWearable(wearable_type); + + LLUUID linked_id = item->getLinkedUUID(); + LLViewerInventoryItem* linked_item = gInventory.getItem(linked_id); + can_favorite |= !linked_item->getIsFavorite(); + can_unfavorite |= linked_item->getIsFavorite(); + if (is_worn) { ++n_worn; @@ -1009,7 +1020,7 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu if (can_be_worn) { - can_be_worn = get_can_item_be_worn(item->getLinkedUUID()); + can_be_worn = get_can_item_be_worn(linked_id); } } // for @@ -1031,6 +1042,8 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu setMenuItemEnabled(menu, "create_new", LLAppearanceMgr::instance().canAddWearables(ids)); setMenuItemVisible(menu, "show_original", !standalone); setMenuItemEnabled(menu, "show_original", n_items == 1 && n_links == n_items); + setMenuItemVisible(menu, "favorites_add", can_favorite); + setMenuItemVisible(menu, "favorites_remove", can_unfavorite); setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && n_worn == n_items); setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && n_worn == n_items); setMenuItemVisible(menu, "take_off_or_detach", mask == (MASK_ATTACHMENT|MASK_CLOTHING)); diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 7a5f29020e..915a557239 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -95,6 +95,7 @@ public: * Updates item name and (worn) suffix. */ /*virtual*/ void updateItem(const std::string& name, + bool favorite, EItemState item_state = IS_DEFAULT); void onAddWearable(); @@ -148,6 +149,7 @@ public: /** Set item title. Joint name is added to the title in parenthesis */ /*virtual*/ void updateItem(const std::string& name, + bool favorite, EItemState item_state = IS_DEFAULT); protected: diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 5142e8ceff..2ad285eb1f 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -453,6 +453,9 @@ + diff --git a/indra/newview/skins/default/textures/icons/Icon_Pointer.png b/indra/newview/skins/default/textures/icons/Icon_Pointer.png new file mode 100644 index 0000000000..021942a8aa Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Icon_Pointer.png differ diff --git a/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Content.png b/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Content.png new file mode 100644 index 0000000000..b71b202234 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Content.png differ diff --git a/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Full.png b/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Full.png new file mode 100644 index 0000000000..7d55fb5cfe Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Inv_Favorite_Star_Full.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index ebc4beead2..b18d151ab7 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -306,6 +306,8 @@ with the same filename but different name + + @@ -906,4 +908,5 @@ with the same filename but different name + diff --git a/indra/newview/skins/default/xui/da/floater_search.xml b/indra/newview/skins/default/xui/da/floater_search.xml deleted file mode 100644 index 80a30b1aa1..0000000000 --- a/indra/newview/skins/default/xui/da/floater_search.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Henter... - - - Færdig - - - - - Gentag søgning med "God level" - - - - diff --git a/indra/newview/skins/default/xui/de/floater_search.xml b/indra/newview/skins/default/xui/de/floater_search.xml deleted file mode 100644 index bd39bf2bce..0000000000 --- a/indra/newview/skins/default/xui/de/floater_search.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Wird geladen... - - - Fertig - - - - - Suche wiederholen, um aktuellen Gott-Level zu berücksichtigen - - - - diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml deleted file mode 100644 index 6c3214a76d..0000000000 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ /dev/null @@ -1,421 +0,0 @@ - - - - (unknown) - - - (public) - - - You can: - - - Owner can: - - - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] - - - - Name: - - - - Description: - - - - Creator: - - - TestString PleaseIgnore - -