diff --git a/.github/workflows/build_viewer.yml b/.github/workflows/build_viewer.yml
index 1b65b9e791..790502eb8a 100644
--- a/.github/workflows/build_viewer.yml
+++ b/.github/workflows/build_viewer.yml
@@ -35,12 +35,9 @@ jobs:
build_matrix:
strategy:
matrix:
- os: [macos-15,ubuntu-24.04,windows-2022]
+ os: [macos-15,ubuntu-22.04,windows-2022]
grid: [sl,os]
variant: [regular, avx]
- include:
- - os: ubuntu-24.04
- container_image: ubuntu:22.04 # only Linux uses a container
runs-on: ${{ matrix.os }}
container: ${{ matrix.container_image }}
outputs:
@@ -128,17 +125,16 @@ jobs:
with:
swap-storage: false
- - name: Install GCC-14
+ - name: Install GCC-13
if: runner.os == 'Linux'
run: |
sudo apt-get update
- sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update
- sudo apt-get install -y gcc-14 g++-14
- echo "CC=gcc-14" >> $GITHUB_ENV
- echo "CXX=g++-14" >> $GITHUB_ENV
-
+ sudo apt-get install -y gcc-13 g++-13
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
+ --slave /usr/bin/g++ g++ /usr/bin/g++-13
+ echo "CC=gcc-13" >> $GITHUB_ENV
+ echo "CXX=g++-13" >> $GITHUB_ENV
- name: Setup rclone and download the folder
uses: beqjanus/setup-rclone@main
with:
diff --git a/.github/workflows/qatest.yaml b/.github/workflows/qatest.yaml
index 5d8894a3f4..4e10900441 100644
--- a/.github/workflows/qatest.yaml
+++ b/.github/workflows/qatest.yaml
@@ -46,11 +46,10 @@ jobs:
runner: qa-dan-asus
artifact: Windows-installer
install-path: 'C:\viewer-automation-main'
- # Commented out until mac runner is available
- # - os: mac
- # runner: qa-mac-atlas
- # artifact: Mac-installer
- # install-path: '$HOME/Documents/viewer-automation'
+ - os: mac
+ runner: qa-mac-atlas
+ artifact: macOS-installer
+ install-path: '$HOME/Documents/viewer-automation'
fail-fast: false
runs-on: [self-hosted, "${{ matrix.runner }}"]
@@ -498,11 +497,13 @@ jobs:
mkdir -p "$MOUNT_POINT"
# Mount the DMG
- hdiutil attach "${{ env.INSTALLER_PATH }}" -mountpoint "$MOUNT_POINT" -nobrowse
+ hdiutil attach "$INSTALLER_PATH" -mountpoint "$MOUNT_POINT" -nobrowse
echo "✅ DMG mounted at $MOUNT_POINT"
- # Find the app in the mounted DMG
+ echo "Installing application to default location from DMG..."
+
+ # Find the .app bundle in the DMG
APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1)
if [ -z "$APP_PATH" ]; then
@@ -510,18 +511,40 @@ jobs:
exit 1
fi
- echo "Installing application to Applications folder..."
+ APP_NAME=$(basename "$APP_PATH")
+ DEST_PATH="/Applications/$APP_NAME"
- # Copy the app to the Applications folder (or specified install path)
- cp -R "$APP_PATH" "${{ matrix.install-path }}"
+ # Handle existing installation
+ if [ -d "$DEST_PATH" ]; then
+ echo "Found existing installation at: $DEST_PATH"
+ echo "Moving existing installation to trash..."
+
+ # Move to trash instead of force removing
+ TRASH_PATH="$HOME/.Trash/$(date +%Y%m%d_%H%M%S)_$APP_NAME"
+ mv "$DEST_PATH" "$TRASH_PATH" || {
+ echo "⚠️ Could not move to trash, trying direct removal..."
+ rm -rf "$DEST_PATH" || {
+ echo "❌ Could not remove existing installation"
+ echo "Please manually remove: $DEST_PATH"
+ exit 1
+ }
+ }
+
+ echo "✅ Existing installation handled successfully"
+ fi
+
+ # Copy the .app to /Applications
+ echo "Copying app from: $APP_PATH"
+ echo "To destination: /Applications/"
+ cp -R "$APP_PATH" /Applications/
# Verify the app was copied successfully
- if [ ! -d "${{ matrix.install-path }}/$(basename "$APP_PATH")" ]; then
- echo "❌ Error: Failed to install application to ${{ matrix.install-path }}!"
+ if [ ! -d "$DEST_PATH" ]; then
+ echo "❌ Error: Failed to install application to /Applications!"
exit 1
fi
- echo "✅ Application installed successfully to ${{ matrix.install-path }}"
+ echo "✅ Application installed successfully to /Applications"
# Save mount point for cleanup
echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV
diff --git a/autobuild.xml b/autobuild.xml
index 743f4b47bc..dd34a98ffe 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2649,23 +2649,17 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
threejs
tinygltf
@@ -3215,6 +3187,64 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
version
1.0.9-5e8947c
+ discord_sdk
+
+ platforms
+
+ windows64
+
+ archive
+
+ creds
+ github
+ hash
+ e11571bf76b27d15c244069988ae372eaa5afae9
+ hash_algorithm
+ sha1
+ url
+ https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333720
+
+ name
+ windows64
+
+ darwin64
+
+ archive
+
+ creds
+ github
+ hash
+ dc21df8b051c425163acf3eff8f06e32f407c9e0
+ hash_algorithm
+ sha1
+ url
+ https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333706
+
+ name
+ darwin64
+
+
+ license
+ discord_sdk
+ license_file
+ LICENSES/discord_sdk.txt
+ copyright
+ Discord Inc.
+ version
+ 1.4.9649.16733550144
+ name
+ discord_sdk
+ vcs_branch
+ main
+ vcs_revision
+ ef5c7c4a490ceac2df2b2f046788b1daf1bbb392
+ vcs_url
+ https://github.com/secondlife/3p-discord-sdk
+ canonical_repo
+ https://github.com/secondlife/3p-discord-sdk
+ description
+ Discord Social SDK
+
package_description
@@ -3236,6 +3266,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE
-DROOT_PROJECT_NAME:STRING=SecondLife
-DINSTALL_PROPRIETARY=TRUE
+ -DUSE_OPENAL:BOOL=ON
build
@@ -3277,6 +3308,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE
-DROOT_PROJECT_NAME:STRING=SecondLife
-DINSTALL_PROPRIETARY=TRUE
+ -DUSE_OPENAL:BOOL=ON
build
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 6b73b366d3..e05b8c470e 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -19,7 +19,7 @@ set(cmake_SOURCE_FILES
Copy3rdPartyLibs.cmake
DBusGlib.cmake
DeploySharedLibs.cmake
- Discord.cmake # Discord rich presence
+ Discord.cmake
DragDrop.cmake
EXPAT.cmake
FindAutobuild.cmake
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 4b74da600c..424d7e6ff0 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -6,6 +6,9 @@
include(CMakeCopyIfDifferent)
include(Linking)
+if (USE_DISCORD)
+ include(Discord)
+endif ()
include(OPENAL)
include(FMODSTUDIO)
@@ -83,6 +86,10 @@ if(WINDOWS)
endif(ADDRESS_SIZE EQUAL 32)
endif (USE_BUGSPLAT)
+ if (TARGET ll::discord_sdk)
+ list(APPEND release_files discord_partner_sdk.dll)
+ endif ()
+
set(release_files ${release_files} growl++.dll growl.dll )
if (TARGET ll::fmodstudio)
# fmodL is included for logging, only one should be picked by manifest
@@ -198,6 +205,10 @@ elseif(DARWIN)
)
endif()
+ if (TARGET ll::discord_sdk)
+ list(APPEND release_files libdiscord_partner_sdk.dylib)
+ endif ()
+
if (TARGET ll::fmodstudio)
set(debug_files ${debug_files} libfmodL.dylib)
set(release_files ${release_files} libfmod.dylib)
diff --git a/indra/cmake/Discord.cmake b/indra/cmake/Discord.cmake
index f9f97a4dc7..9741ff17f0 100644
--- a/indra/cmake/Discord.cmake
+++ b/indra/cmake/Discord.cmake
@@ -1,5 +1,19 @@
# -*- cmake -*-
+# Prefer FS-specific Discord implementation
+#include(Prebuilt)
+#
+#include_guard()
+#
+#add_library(ll::discord_sdk INTERFACE IMPORTED)
+#target_compile_definitions(ll::discord_sdk INTERFACE LL_DISCORD=1)
+#
+#use_prebuilt_binary(discord_sdk)
+#
+#target_include_directories(ll::discord_sdk SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/discord_sdk)
+#target_link_libraries(ll::discord_sdk INTERFACE discord_partner_sdk)
+#
+
include_guard()
add_library(fs::discord INTERFACE IMPORTED)
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 2c4b5bc7ce..9aeb6d804d 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 a76dbdb8bb..18af866141 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 b4967b1f81..8d99975d84 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -556,6 +556,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/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
index 8ac28daeb8..b1d7eac943 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 1e60713b6d..5cccc701a4 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 807e9c70d2..92374e88f7 100644
--- a/indra/llimage/llimagejpeg.cpp
+++ b/indra/llimage/llimagejpeg.cpp
@@ -492,6 +492,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 af7d6c0610..2bd6239dd3 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -956,6 +956,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 db17b538a0..1be616f5fe 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"
///----------------------------------------------------------------------------
@@ -222,3 +223,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 ec97ab5016..e55c41eee9 100644
--- a/indra/llinventory/llfoldertype.h
+++ b/indra/llinventory/llfoldertype.h
@@ -123,6 +123,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 8098ff19c2..9c40acaa62 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))
{
@@ -813,6 +845,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))
{
@@ -957,7 +1006,7 @@ bool LLInventoryItem::exportLegacyStream(std::ostream& output_stream, bool inclu
LLSD LLInventoryItem::asLLSD() const
{
- LLSD sd = LLSD();
+ LLSD sd;
asLLSD(sd);
return sd;
}
@@ -966,13 +1015,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()))
@@ -987,19 +1041,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)
@@ -1015,6 +1072,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;
@@ -1060,9 +1119,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;
}
@@ -1255,6 +1325,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;
}
@@ -1266,11 +1341,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;
}
@@ -1318,6 +1399,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))
{
@@ -1440,6 +1532,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
{
@@ -1474,12 +1583,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;
@@ -1487,49 +1595,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 23fca713ca..84fde40a1f 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
@@ -1016,55 +1089,30 @@ std::string mask_to_string(U32 mask, bool isOpenSim /*=false*/) // rem
///----------------------------------------------------------------------------
/// 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 ac3c247f56..469eacc5ef 100644
--- a/indra/llinventory/llpermissions.h
+++ b/indra/llinventory/llpermissions.h
@@ -302,6 +302,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;
@@ -447,6 +449,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 1444e36179..e48eabb5c0 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -687,6 +687,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/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 420911abe6..5c3b7ded7a 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -5760,7 +5760,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 61f13aefbe..58e8c2d5d5 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)
@@ -510,7 +457,7 @@ inline LLVector4 operator-(const LLVector4& a)
// [RLVa:KB] - RlvBehaviourModifierCompMin/Max
inline bool operator<(const LLVector4& lhs, const LLVector4& rhs)
{
- return std::tie(lhs.mV[0], lhs.mV[1], lhs.mV[2], lhs.mV[3]) < std::tie(rhs.mV[0], rhs.mV[1], rhs.mV[2], rhs.mV[3]);
+ return std::tie(lhs.mV[VX], lhs.mV[VY], lhs.mV[VZ], lhs.mV[VW]) < std::tie(rhs.mV[VX], rhs.mV[VY], rhs.mV[VZ], rhs.mV[VW]);
}
// [/RLVa:KB]
@@ -524,16 +471,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)
@@ -545,17 +492,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
{
@@ -564,30 +507,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/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index e9a1d0abd6..d273d6ddca 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -69,7 +69,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/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 aae8f1c18a..e7677643c1 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -70,7 +70,7 @@ const LLRect& LLFlatListView::getItemsRect() const
bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
{
if (!item) return false;
- if (value.isUndefined()) return false;
+ if (value.isUndefined()) return false; // item stays an orphan?!!!
//force uniqueness of items, easiest check but unreliable
if (item->getParent() == mItemsPanel) return false;
@@ -1370,9 +1370,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)
@@ -1440,6 +1448,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 c650458d44..dc648d2d75 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -498,7 +498,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/llfloater.cpp b/indra/llui/llfloater.cpp
index c33bf14354..548ee2037d 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -338,8 +338,10 @@ void LLFloater::initFloater(const Params& p)
// Help button: '?'
//SL-14050 Disable all Help question marks
- // Nope!
- mButtonsEnabled[BUTTON_HELP] = !mHelpTopic.empty();// false;
+ // Disable the help button only if the debug setting is on
+ static LLUICachedControl hide_help_buttons ("FSHideHelpButtons", false);
+ mButtonsEnabled[BUTTON_HELP] = !mHelpTopic.empty() && !hide_help_buttons;// false;
+ //
// Minimize button only for top draggers
if ( !mDragOnLeft && mCanMinimize )
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 1810d89494..12efc01c76 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -226,6 +226,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 8c13002488..c0a8d5bada 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"
@@ -62,7 +63,11 @@ LLUIColor LLFolderViewItem::sProtectedColor;
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;
@@ -70,6 +75,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)
@@ -104,6 +112,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);
// Make inventory selection color independent from menu color
@@ -132,6 +142,8 @@ void LLFolderViewItem::cleanupClass()
sFonts.clear();
sFolderArrowImg = nullptr;
sSelectionImg = nullptr;
+ sFavoriteImg = nullptr;
+ sFavoriteContentImg = nullptr;
sSuffixFont = nullptr;
}
@@ -140,13 +152,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"),
@@ -169,6 +183,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 ),
@@ -189,7 +205,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),
@@ -206,6 +222,21 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
mForInventory(p.for_inventory),
mItemTopPad(p.item_top_pad)
{
+ 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);
@@ -228,6 +259,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)
@@ -342,6 +374,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()
@@ -376,6 +409,8 @@ void LLFolderViewItem::refreshSuffix()
mIconOpen = vmi->getIconOpen();
mIconOverlay = vmi->getIconOverlay();
+ mIsFavorite = vmi->isFavorite() && !vmi->isItemInTrash();
+
if (mRoot->useLabelSuffix())
{
// Very Expensive!
@@ -463,6 +498,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);
@@ -589,10 +628,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)
@@ -837,6 +881,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;
@@ -994,6 +1077,7 @@ void LLFolderViewItem::draw()
{
drawOpenFolderArrow();
}
+ drawFavoriteIcon();
drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
@@ -1065,7 +1149,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())
{
@@ -1203,7 +1300,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)
{
}
@@ -1229,6 +1327,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
@@ -1872,6 +1975,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 474edaec04..a6fb9381a5 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;
@@ -97,6 +99,8 @@ protected:
LLWString mLabel;
S32 mLabelWidth;
bool mLabelWidthDirty;
+ bool mIsFavorite;
+ bool mHasFavorites;
S32 mLabelPaddingRight;
LLFolderViewFolder* mParentFolder;
LLPointer mViewModelItem;
@@ -126,7 +130,7 @@ protected:
mIsCurSelection,
mDragAndDropTarget,
mIsMouseOverTitle,
- mAllowWear,
+ mMarketplaceItem,
mAllowDrop,
mSingleFolderMode,
mDoubleClickOverride,
@@ -137,6 +141,7 @@ protected:
LLUIColor mFontColor;
LLUIColor mFontHighlightColor;
+ static bool sColorSetInitialized;
// Inventory specials
bool mForInventory;
@@ -153,9 +158,11 @@ protected:
static LLUIColor sFilterTextColor;
static LLUIColor sSuffixColor;
static LLUIColor sSearchStatusColor;
+ static LLUIColor sFavoriteColor;
// Special for protected items
static LLUIColor sProtectedColor;
+
// this is an internal method used for adding items to folders. A
// no-op at this level, but reimplemented in derived classes.
virtual void addItem(LLFolderViewItem*) { }
@@ -218,6 +225,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();
@@ -308,6 +317,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,
@@ -326,6 +336,8 @@ private:
static S32 sTopPad;
static LLUIImagePtr sFolderArrowImg;
static LLUIImagePtr sSelectionImg;
+ static LLUIImagePtr sFavoriteImg;
+ static LLUIImagePtr sFavoriteContentImg;
static LLFontGL* sSuffixFont;
LLFontVertexBuffer mLabelFontBuffer;
@@ -415,6 +427,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 9cdd28485b..c971c70314 100644
--- a/indra/llui/llfolderviewmodel.h
+++ b/indra/llui/llfolderviewmodel.h
@@ -163,6 +163,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;
@@ -172,6 +173,7 @@ public:
virtual void move( LLFolderViewModelItem* parent_listener ) = 0;
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;
@@ -184,6 +186,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?
@@ -220,6 +225,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;
//
@@ -259,14 +265,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)
{
@@ -281,7 +287,7 @@ public:
mParent->dirtyFilter();
}
}
- void dirtyDescendantsFilter()
+ void dirtyDescendantsFilter() override
{
mMostFilteredDescendantGeneration = -1;
if (mParent)
@@ -289,13 +295,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);
@@ -303,15 +309,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); });
@@ -324,7 +330,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;
@@ -333,20 +339,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)
{
@@ -357,7 +363,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)
{
@@ -366,10 +372,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; }
//
virtual LLFolderViewModelItem* getParent() const { return mParent; }
@@ -389,7 +395,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;
};
@@ -403,15 +409,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);
@@ -441,14 +447,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
{
@@ -469,7 +475,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 42451bd83f..608f9e3491 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"
@@ -1432,6 +1433,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 eac7da1692..0e0d1604b7 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1215,6 +1215,14 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
+ S32 text_length = (S32)getLength();
+ if (pos >= text_length || pos < 0)
+ {
+ return 0; // nothing to remove
+ }
+ // Clamp length to not go past the end of the text
+ length = std::min(length, text_length - pos);
+
beforeValueChange();
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
@@ -2407,6 +2415,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url));
registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, 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));
registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 3de763de1a..d3478e10e0 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -276,6 +276,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mEnableTooltipPaste(p.enable_tooltip_paste),
mPassDelete(false),
mKeepSelectionOnReturn(false),
+ mSelectAllOnFocusReceived(false),
+ mSelectedOnFocusReceived(false),
mEnableTabRemove(p.enable_tab_remove) // FIRE-15591: Optional tab remove
{
mSourceID.generate();
@@ -450,6 +452,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insen
// [/SL:KB]
mIsSelecting = true;
+ mSelectedOnFocusReceived = false;
mSelectionEnd = mCursorPos;
mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
}
@@ -741,6 +744,13 @@ bool LLTextEditor::canSelectAll() const
return true;
}
+//virtual
+void LLTextEditor::deselect()
+{
+ LLTextBase::deselect();
+ mSelectedOnFocusReceived = false;
+}
+
// virtual
void LLTextEditor::selectAll()
{
@@ -759,6 +769,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)
{
static LLUICachedControl useBWEmojis( "FSUseBWEmojis", false); // Add B&W emoji font support
@@ -861,8 +876,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;
@@ -2400,6 +2423,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 6f152d43a0..b71f54fc15 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -145,8 +145,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();
@@ -356,6 +358,8 @@ private:
bool mEnableTooltipPaste;
bool mPassDelete;
bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter
+ bool mSelectAllOnFocusReceived;
+ bool mSelectedOnFocusReceived;
bool mEnableTabRemove; // FIRE-15591: Optional tab remove
LLUUID mSourceID;
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index d2cd5cbe6b..7b1cfe9d21 100644
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -30,6 +30,7 @@
#include "llview.h"
#include "llwindow.h"
#include "llurlregistry.h"
+#include "v3dmath.h"
// global state for the callback functions
@@ -128,6 +129,23 @@ void LLUrlAction::showLocationOnMap(std::string url)
}
}
+void LLUrlAction::showParcelOnMap(std::string url)
+{
+ LLSD path_array = LLURI(url).pathArray();
+ auto path_parts = path_array.size();
+
+ if (path_parts < 3) // no parcel id
+ {
+ LL_WARNS() << "Global coordinates are missing in url: [" << url << "]" << LL_ENDL;
+ return;
+ }
+
+ LLVector3d parcel_pos = LLUrlEntryParcel::getParcelPos(LLUUID(LLURI::unescape(path_array[2])));
+ std::ostringstream pos;
+ pos << parcel_pos.mdV[VX] << '/' << parcel_pos.mdV[VY] << '/' << parcel_pos.mdV[VZ];
+ executeSLURL("secondlife:///app/worldmap_global/" + pos.str());
+}
+
void LLUrlAction::copyURLToClipboard(std::string url)
{
LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
@@ -142,6 +160,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 3ce8b37db3..0e0ad7a85a 100644
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -63,6 +63,8 @@ public:
/// if the Url specifies an SL location, show it on a map
static void showLocationOnMap(std::string url);
+ static void showParcelOnMap(std::string url);
+
/// perform the appropriate action for left-clicking on a Url
static void clickAction(std::string url, bool trusted_content);
@@ -72,6 +74,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 e6f068ab95..ceb47f3f59 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -41,11 +41,7 @@
#include "lluicolortable.h"
#include "message.h"
#include "llexperiencecache.h"
-
-// hop:// protocol>
-//#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
-#define APP_HEADER_REGEX "(((hop|x-grid-location-info)://[-\\w\\.\\:\\@]+/app)|((hop|secondlife):///app))"
-//
+#include "v3dmath.h"
// Utility functions
std::string localize_slapp_label(const std::string& url, const std::string& full_name);
@@ -1202,6 +1198,7 @@ LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
LLHost LLUrlEntryParcel::sRegionHost;
bool LLUrlEntryParcel::sDisconnected(false);
std::set LLUrlEntryParcel::sParcelInfoObservers;
+std::map LLUrlEntryParcel::sParcelPos;
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
@@ -1294,6 +1291,20 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
}
}
+ if (sParcelPos.find(parcel_data.parcel_id) == sParcelPos.end())
+ {
+ sParcelPos[parcel_data.parcel_id] = LLVector3d(parcel_data.global_x, parcel_data.global_y, parcel_data.global_z);
+ }
+}
+
+// static
+LLVector3d LLUrlEntryParcel::getParcelPos(const LLUUID& parcel_id)
+{
+ if (sParcelPos.find(parcel_id) != sParcelPos.end())
+ {
+ return sParcelPos[parcel_id];
+ }
+ return LLVector3d();
}
//
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index b195a3c77c..6cd83507f2 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -41,6 +41,12 @@
#include
class LLAvatarName;
+class LLVector3d;
+
+// hop:// protocol>
+//#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
+#define APP_HEADER_REGEX "(((hop|x-grid-location-info)://[-\\w\\.\\:\\@]+/app)|((hop|secondlife):///app))"
+//
typedef boost::signals2::signal sParcelInfoObservers;
+ static std::map sParcelPos;
};
///
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index ff84e5b78e..81dea9bef2 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -670,7 +670,10 @@ LLWebRTCPeerConnectionInterface *LLWebRTCImpl::newPeerConnection()
peerConnection->init(this);
mPeerConnections.emplace_back(peerConnection);
- peerConnection->enableSenderTracks(!mMute);
+ // Should it really start disabled?
+ // Seems like something doesn't get the memo and senders need to be reset later
+ // to remove the voice indicator from taskbar
+ peerConnection->enableSenderTracks(false);
if (mPeerConnections.empty())
{
setRecording(true);
@@ -704,7 +707,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn
LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
mWebRTCImpl(nullptr),
mPeerConnection(nullptr),
- mMute(true),
+ mMute(MUTE_INITIAL),
mAnswerReceived(false)
{
}
@@ -739,6 +742,19 @@ void LLWebRTCPeerConnectionImpl::terminate()
}
}
+ // to remove 'Secondlife is recording' icon from taskbar
+ // if user was speaking
+ auto senders = mPeerConnection->GetSenders();
+ for (auto& sender : senders)
+ {
+ auto track = sender->track();
+ if (track)
+ {
+ track->set_enabled(false);
+ }
+ }
+ mPeerConnection->SetAudioRecording(false);
+
mPeerConnection->Close();
if (mLocalStream)
{
@@ -828,6 +844,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
audioOptions.auto_gain_control = true;
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
+ audioOptions.init_recording_on_send = false;
mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream");
@@ -887,6 +904,7 @@ 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)
{
@@ -932,12 +950,23 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
void LLWebRTCPeerConnectionImpl::setMute(bool mute)
{
- mMute = mute;
+ EMicMuteState new_state = mute ? MUTE_MUTED : MUTE_UNMUTED;
+ if (new_state == mMute)
+ {
+ return; // no change
+ }
+ bool force_reset = mMute == MUTE_INITIAL && mute;
+ bool enable = !mute;
+ mMute = new_state;
+
mWebRTCImpl->PostSignalingTask(
- [this]()
+ [this, force_reset, enable]()
{
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();
@@ -946,7 +975,14 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
auto track = sender->track();
if (track)
{
- track->set_enabled(!mMute);
+ if (force_reset)
+ {
+ // Force notify observers
+ // Was it disabled too early?
+ // Without this microphone icon in Win's taskbar will stay
+ track->set_enabled(true);
+ }
+ track->set_enabled(enable);
}
}
}
@@ -955,7 +991,17 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
void LLWebRTCPeerConnectionImpl::resetMute()
{
- setMute(mMute);
+ switch(mMute)
+ {
+ case MUTE_MUTED:
+ setMute(true);
+ break;
+ case MUTE_UNMUTED:
+ setMute(false);
+ break;
+ default:
+ break;
+ }
}
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
index b93a1fdb01..b6294dbd4a 100644
--- a/indra/llwebrtc/llwebrtc_impl.h
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -417,7 +417,12 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
rtc::scoped_refptr mPeerConnectionFactory;
- bool mMute;
+ typedef enum {
+ MUTE_INITIAL,
+ MUTE_MUTED,
+ MUTE_UNMUTED,
+ } EMicMuteState;
+ EMicMuteState mMute;
// signaling
std::vector mSignalingObserverList;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 73b9f75f16..befe119a9f 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -76,6 +76,11 @@
#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems
#pragma comment(lib, "dinput8")
+#pragma comment(lib, "UxTheme.lib")
+#pragma comment(lib, "Dwmapi.lib")
+#include
+#include // needed for DwmSetWindowAttribute to set window theme
+
const S32 MAX_MESSAGE_PER_UPDATE = 20;
const S32 BITS_PER_PIXEL = 32;
const S32 MAX_NUM_RESOLUTIONS = 32;
@@ -85,6 +90,10 @@ const F32 ICON_FLASH_TIME = 0.5f;
#define USER_DEFAULT_SCREEN_DPI 96 // Win7
#endif
+#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
+#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
+#endif
+
// Claim a couple unused GetMessage() message IDs
const UINT WM_DUMMY_(WM_USER + 0x0017);
const UINT WM_POST_FUNCTION_(WM_USER + 0x0018);
@@ -104,6 +113,7 @@ static std::thread::id sMainThreadId;
LPWSTR gIconResource = IDI_APPLICATION;
+LPWSTR gIconSmallResource = IDI_APPLICATION;
LPDIRECTINPUT8 gDirectInput8;
LLW32MsgCallback gAsyncMsgCallback = NULL;
@@ -137,6 +147,17 @@ typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)(
_Out_ UINT *dpiX,
_Out_ UINT *dpiY);
+typedef enum PREFERRED_APP_MODE
+{
+ DEFAULT,
+ ALLOW_DARK,
+ FORCE_DARK,
+ FORCE_LIGHT,
+ MAX
+} PREFERRED_APP_MODE;
+
+typedef PREFERRED_APP_MODE(WINAPI* fnSetPreferredAppMode)(PREFERRED_APP_MODE mode);
+
//
// LLWindowWin32
//
@@ -350,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()
{
@@ -410,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;
};
@@ -508,6 +534,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFSAASamples = fsaa_samples;
mIconResource = gIconResource;
+ mIconSmallResource = gIconSmallResource;
mOverrideAspectRatio = 0.f;
mNativeAspectRatio = 0.f;
mInputProcessingPaused = false;
@@ -849,6 +876,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
// Initialize (boot strap) the Language text input management,
// based on the system's (or user's) default settings.
allowLanguageTextInput(NULL, false);
+ updateWindowTheme();
+ setCustomIcon();
}
@@ -860,6 +889,7 @@ LLWindowWin32::~LLWindowWin32()
}
delete mDragDrop;
+ mDragDrop = NULL;
delete [] mWindowTitle;
mWindowTitle = NULL;
@@ -871,6 +901,7 @@ LLWindowWin32::~LLWindowWin32()
mWindowClassName = NULL;
delete mWindowThread;
+ mWindowThread = NULL;
}
void LLWindowWin32::show()
@@ -979,7 +1010,7 @@ void LLWindowWin32::close()
// Restore gamma to the system values.
restoreGamma();
- LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
+ LL_INFOS("Window") << "Cleanup and destruction of Window Thread" << LL_ENDL;
if (sWindowHandleForMessageBox == mWindowHandle)
{
@@ -989,7 +1020,11 @@ void LLWindowWin32::close()
mhDC = NULL;
mWindowHandle = NULL;
- mWindowThread->wakeAndDestroy();
+ if (mWindowThread->wakeAndDestroy())
+ {
+ // thread will delete itselfs once done
+ mWindowThread = NULL;
+ }
}
bool LLWindowWin32::isValid()
@@ -1528,10 +1563,10 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
LL_INFOS("Window") << "pixel formats done." << LL_ENDL ;
- S32 swap_method = 0;
+ S32 swap_method = 0;
S32 cur_format = 0;
const S32 max_format = (S32)num_formats - 1;
- GLint swap_query = WGL_SWAP_METHOD_ARB;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
// SL-14705 Fix name tags showing in front of objects with AMD GPUs.
// On AMD hardware we need to iterate from the first pixel format to the end.
@@ -1754,6 +1789,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
// important to call DestroyWindow() from the window thread
if (!destroy_window_handler(oldWindowHandle))
{
+
LL_WARNS("Window") << "Failed to properly close window before recreating it!"
<< LL_ENDL;
}
@@ -3112,6 +3148,17 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
WINDOW_IMP_POST(window_imp->mMouseVanish = true);
}
}
+ // Check if theme-related settings changed
+ else if (l_param && (wcscmp((LPCWSTR)l_param, L"ImmersiveColorSet") == 0))
+ {
+ WINDOW_IMP_POST(window_imp->updateWindowTheme());
+ }
+ }
+ break;
+
+ case WM_DWMCOLORIZATIONCOLORCHANGED:
+ {
+ WINDOW_IMP_POST(window_imp->updateWindowTheme());
}
break;
@@ -3204,10 +3251,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
@@ -4735,25 +4786,11 @@ void LLWindowWin32::getWindowChrome( U32 &aChromeW, U32 &aChromeH )
#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
@@ -4919,6 +4956,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
{
sWindowThreadId = std::this_thread::get_id();
LogChange logger("Window");
+
//as good a place as any to up the MM timer resolution (see ms_sleep)
//attempt to set timer resolution to 1ms
TIMECAPS tc;
@@ -4956,6 +4994,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
", ", msg.wParam, ")");
TranslateMessage(&msg);
DispatchMessage(&msg);
+
mMessageQueue.pushFront(msg);
}
}
@@ -4975,108 +5014,102 @@ 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);
+ // Stop checking budget
+ mGLReady = false;
- // Schedule destruction
+ // 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);
+ }
- LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL;
+ // Signal thread to clean up when done
+ mDeleteOnExit = true;
+
+ LL_INFOS("Window") << "Detaching window's thread" << LL_ENDL;
+ // Cleanly detach threads instead of joining them to avoid blocking the main thread
+ // This is acceptable since the thread will self-delete with mDeleteOnExit
+ // Doing it before close() to make sure thread doesn't die before or mid detach.
+ for (auto& pair : mThreads)
+ {
+ 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;
+ }
+ }
+
+ // Close the queue.
+ LL_INFOS("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)
- {
- ms_sleep(100);
- }
-
- 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;
+ LL_INFOS("Window") << "Thread pool shutdown complete" << LL_ENDL;
+ return true;
}
void LLWindowWin32::post(const std::function& func)
@@ -5137,3 +5170,69 @@ void LLWindowWin32::updateWindowRect()
});
}
}
+
+bool LLWindowWin32::isSystemAppDarkMode()
+{
+ HKEY hKey;
+ DWORD dwValue = 1; // Default to light theme
+ DWORD dwSize = sizeof(DWORD);
+
+ // Check registry for system theme preference
+ LSTATUS ret_code =
+ RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey);
+ if (ERROR_SUCCESS == ret_code)
+ {
+ if (RegQueryValueExW(hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) != ERROR_SUCCESS)
+ {
+ // If AppsUseLightTheme is not found, check SystemUsesLightTheme
+ dwSize = sizeof(DWORD);
+ RegQueryValueExW(hKey, L"SystemUsesLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize);
+ }
+ RegCloseKey(hKey);
+ }
+
+ // Return true if dark mode
+ return dwValue == 0;
+}
+
+void LLWindowWin32::updateWindowTheme()
+{
+ bool use_dark_mode = isSystemAppDarkMode();
+ if (use_dark_mode == mCurrentDarkMode)
+ {
+ return;
+ }
+ mCurrentDarkMode = use_dark_mode;
+
+ HMODULE hUxTheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (hUxTheme)
+ {
+ auto SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxTheme, "SetPreferredAppMode");
+ if (SetPreferredAppMode)
+ {
+ SetPreferredAppMode(use_dark_mode ? ALLOW_DARK : FORCE_LIGHT);
+ }
+ FreeLibrary(hUxTheme);
+ }
+ BOOL dark_mode(use_dark_mode);
+ DwmSetWindowAttribute(mWindowHandle, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_mode, sizeof(dark_mode));
+
+ LL_INFOS("Window") << "Viewer window theme is set to " << (use_dark_mode ? "dark" : "light") << " mode" << LL_ENDL;
+}
+
+void LLWindowWin32::setCustomIcon()
+{
+ if (mWindowHandle)
+ {
+ HICON hDefaultIcon = LoadIcon(mhInstance, mIconResource);
+ HICON hSmallIcon = LoadIcon(mhInstance, mIconSmallResource);
+ mWindowThread->post([=]()
+ {
+ SendMessage(mWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hDefaultIcon);
+ SendMessage(mWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon);
+
+ SetClassLongPtr(mWindowHandle, GCLP_HICON, (LONG_PTR)hDefaultIcon);
+ SetClassLongPtr(mWindowHandle, GCLP_HICONSM, (LONG_PTR)hSmallIcon);
+ });
+ }
+}
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index bd2b0db033..db09dd06ae 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -217,6 +217,7 @@ protected:
bool mCustomGammaSet;
LPWSTR mIconResource;
+ LPWSTR mIconSmallResource;
bool mInputProcessingPaused;
// The following variables are for Language Text Input control.
@@ -249,6 +250,11 @@ protected:
RECT mRect;
RECT mClientRect;
+ void updateWindowTheme();
+ bool isSystemAppDarkMode();
+ void setCustomIcon();
+ bool mCurrentDarkMode { false };
+
struct LLWindowWin32Thread;
LLWindowWin32Thread* mWindowThread = nullptr;
LLThreadSafeQueue> mFunctionQueue;
@@ -288,6 +294,7 @@ private:
extern LLW32MsgCallback gAsyncMsgCallback;
extern LPWSTR gIconResource;
+extern LPWSTR gIconSmallResource;
S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type);
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index f11593850e..cca1fa4fd1 100644
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -165,7 +165,10 @@ LLControlVariable::LLControlVariable(const std::string& name, eControlType type,
{
if ((persist != PERSIST_NO) && mComment.empty())
{
- LL_ERRS() << "Must supply a comment for control " << mName << ". Please perform a clean installation!" << LL_ENDL;
+ // 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
mValues.push_back(initial);
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 1ada17a8d8..ae16fda157 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -15,6 +15,9 @@ include(CMakeCopyIfDifferent)
include(CubemapToEquirectangularJS)
include(GLIB)
include(DragDrop)
+if (USE_DISCORD)
+ include(Discord)
+endif ()
include(EXPAT)
include(FMODSTUDIO)
include(GLOD) # restore GLOD
@@ -467,6 +470,7 @@ set(viewer_SOURCE_FILES
llhudeffectpointat.cpp
llhudeffecttrail.cpp
llhudeffectblob.cpp
+ llhudeffectresetskeleton.cpp
llhudicon.cpp
llhudmanager.cpp
llhudnametag.cpp
@@ -492,6 +496,7 @@ set(viewer_SOURCE_FILES
llinventorygallerymenu.cpp
llinventoryicon.cpp
llinventoryitemslist.cpp
+ llinventorylistener.cpp
llinventorylistitem.cpp
llinventorymodel.cpp
llinventorymodelbackgroundfetch.cpp
@@ -1310,6 +1315,7 @@ set(viewer_HEADER_FILES
llhudeffectpointat.h
llhudeffecttrail.h
llhudeffectblob.h
+ llhudeffectresetskeleton.h
llhudicon.h
llhudmanager.h
llhudnametag.h
@@ -1334,6 +1340,7 @@ set(viewer_HEADER_FILES
llinventorygallerymenu.h
llinventoryicon.h
llinventoryitemslist.h
+ llinventorylistener.h
llinventorylistitem.h
llinventorymodel.h
llinventorymodelbackgroundfetch.h
@@ -2025,6 +2032,7 @@ if (WINDOWS)
res/firestorm_icon.BMP
res/firestorm_icon.ico
res-sdl/firestorm_icon.BMP
+ res/ll_icon_small.ico
res/resource.h
res/toolpickobject.cur
res/toolpickobject2.cur
@@ -2319,6 +2327,12 @@ if (WINDOWS)
)
endif (ADDRESS_SIZE EQUAL 64)
+ if (TARGET ll::discord_sdk)
+ list(APPEND COPY_INPUT_DEPENDENCIES
+ ${SHARED_LIB_STAGING_DIR}/discord_partner_sdk.dll
+ )
+ endif ()
+
if (TARGET ll::fmodstudio)
list(APPEND COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/fmod.dll
@@ -2342,6 +2356,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2383,6 +2398,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2450,6 +2466,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2554,6 +2571,10 @@ target_link_libraries(${VIEWER_BINARY_NAME}
fs::discord # Discord support
)
+if (USE_DISCORD)
+ target_link_libraries(${VIEWER_BINARY_NAME} ll::discord_sdk )
+endif ()
+
if( TARGET ll::intel_memops )
target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )
endif()
@@ -2637,6 +2658,7 @@ if (LINUX)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2665,6 +2687,7 @@ if (LINUX)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2761,6 +2784,7 @@ if (DARWIN)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@@ -2799,6 +2823,7 @@ if (DARWIN)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
+ "--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
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/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e8d6fcdc6a..473ae81e32 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2767,6 +2767,39 @@
Value
1
+ EnableDiscord
+
+ Comment
+ When set, connect to Discord to enable Rich Presence (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
+ ShowDiscordActivityDetails
+
+ Comment
+ When set, show avatar name on Discord Rich Presence (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
+ ShowDiscordActivityState
+
+ Comment
+ When set, show location on Discord Rich Presence (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
EnableDiskCacheDebugInfo
Comment
@@ -2781,13 +2814,13 @@
DiskCachePercentOfTotal
Comment
- The percent of total cache size (defined by CacheSize) to use for the disk cache (UNUSED)
+ The percent of total cache size (defined by CacheSize) to use for the disk cache (ex: asset storage, excludes textures) (UNUSED)
Persist
1
Type
F32
Value
- 40.0
+ 35.0
DiskCacheDirName
@@ -3650,6 +3683,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
@@ -8111,10 +8155,10 @@
Value
0
- FSPoserResetBaseRotationOnEdit
+ FSPoserPelvisUnlockedForBvhSave
Comment
- Whether to reset the base-rotation of a joint to zero when a user edits it.
+ Whether the mPelvis joint should be position/rotationally locked when a BVH is created.
Persist
1
Type
@@ -13194,6 +13238,17 @@ Change of this parameter will affect the layout of buttons in notification toast
Value
1
+ ObscureBalanceInStatusBar
+
+ Comment
+ If true, balance will be shows as '*' (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
RenderUIBuffer
Comment
@@ -18154,7 +18209,7 @@ Change of this parameter will affect the layout of buttons in notification toast
FullScreen
Comment
- run a fullscreen session
+ Run a fullscreen session. MacOS not supported
Persist
1
Type
@@ -18664,10 +18719,32 @@ Change of this parameter will affect the layout of buttons in notification toast
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
@@ -23353,6 +23430,50 @@ Change of this parameter will affect the layout of buttons in notification toast
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
@@ -23463,6 +23584,50 @@ Change of this parameter will affect the layout of buttons in notification toast
Value
31
+ EnableSelectionHints
+
+ Comment
+ Whether or not to send editing hints to animate the arm when editing an object. (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 1
+
+ EnableLookAtTarget
+
+ Comment
+ Whether or not to animate the avatar head and send look at targets when moving the cursor or focusing on objects (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 1
+
+ LimitLookAtTarget
+
+ Comment
+ Whether or not to clamp the look at targets around the avatar head before sending (UNUSED)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
+ LimitLookAtTargetDistance
+
+ Comment
+ Distance to limit look at target to (UNUSED)
+ Persist
+ 1
+ Type
+ F32
+ Value
+ 2
+
FSNetMapPhantomOpacity
@@ -26762,5 +26927,27 @@ Change of this parameter will affect the layout of buttons in notification toast
0
+ FSHideHelpButtons
+
+ Comment
+ When enabled, hides the help button from floaters (requires restart)
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
+ FSResetSkeletonOnStandUp
+
+ Comment
+ Resets own avatar skeleton upon standing up and sends the reset to all surrounding avatars
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 95e203bb67..ce0a20c443 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -602,7 +602,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/fschathistory.cpp b/indra/newview/fschathistory.cpp
index 07566c2f1f..9ca8678b24 100644
--- a/indra/newview/fschathistory.cpp
+++ b/indra/newview/fschathistory.cpp
@@ -1087,7 +1087,7 @@ protected:
menu->setItemEnabled("Chat History", false);
menu->setItemEnabled("Add Contact Set", false);
menu->setItemEnabled("Invite Group", false);
- menu->setItemEnabled("Zoom In", false);
+ menu->setItemEnabled("Zoom In", true);
menu->setItemEnabled("track", false);
menu->setItemEnabled("Share", false);
menu->setItemEnabled("Pay", false);
diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp
index 1fd1bdbd32..7594f57960 100644
--- a/indra/newview/fsfloaterposer.cpp
+++ b/indra/newview/fsfloaterposer.cpp
@@ -60,9 +60,9 @@ constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_ro
constexpr char BVH_JOINT_TRANSFORM_STRING_PREFIX[] = "bvh_joint_transform_";
constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed";
-constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit";
constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso";
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
+constexpr std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave";
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
@@ -193,6 +193,8 @@ bool FSFloaterPoser::postBuild()
mFlipJointBtn = getChild("FlipJoint_avatar");
mRecaptureBtn = getChild("button_RecaptureParts");
mTogglePosingBonesBtn = getChild("toggle_PosingSelectedBones");
+ mToggleLockWorldRotBtn = getChild("toggle_LockWorldRotation");
+ mToggleLockWorldRotBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onClickLockWorldRotBtn(); });
mToggleMirrorRotationBtn = getChild("button_toggleMirrorRotation");
mToggleSympatheticRotationBtn = getChild("button_toggleSympatheticRotation");
@@ -211,9 +213,10 @@ bool FSFloaterPoser::postBuild()
mMiscJointsPnl = getChild("misc_joints_panel");
mCollisionVolumesPnl = getChild("collision_volumes_panel");
+ mUnlockPelvisInBvhSaveCbx = getChild("unlock_pelvis_for_bvh_save_checkbox");
+ mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
mAlsoSaveBvhCbx = getChild("also_save_bvh_checkbox");
- mResetBaseRotCbx = getChild("reset_base_rotation_on_edit_checkbox");
- mResetBaseRotCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSetBaseRotZero(); });
+ mAlsoSaveBvhCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSavingToBvh(); });
mTrackpadSensitivitySpnr = getChild("trackpad_sensitivity_spinner");
mYawSpnr = getChild("limb_yaw_spinner");
@@ -366,10 +369,10 @@ void FSFloaterPoser::onPoseFileSelect()
mPoseSaveNameEditor->setText(name);
bool isDeltaSave = !poseFileStartsFromTeePose(name);
- if (isDeltaSave && hasString("LoadDiffLabel"))
- mLoadPosesBtn->setLabel(getString("LoadDiffLabel"));
- else if (hasString("LoadPoseLabel"))
- mLoadPosesBtn->setLabel(getString("LoadPoseLabel"));
+ if (isDeltaSave)
+ mLoadPosesBtn->setLabel(tryGetString("LoadDiffLabel"));
+ else
+ mLoadPosesBtn->setLabel(tryGetString("LoadPoseLabel"));
}
void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename)
@@ -386,24 +389,21 @@ void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename)
if (getSavingToBvh())
savePoseToBvh(avatar, filename);
- if (hasString(ICON_SAVE_OK))
- mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign());
-
+ mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign());
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
}
else
{
- if (hasString(ICON_SAVE_FAILED))
- mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
- }
+ mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
+ }
}
void FSFloaterPoser::onClickPoseSave()
{
std::string filename = mPoseSaveNameEditor->getValue().asString();
- if (filename.empty() && hasString(ICON_SAVE_FAILED))
+ if (filename.empty())
{
- mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
+ mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
return;
}
@@ -444,8 +444,7 @@ void FSFloaterPoser::onClickPoseSave()
void FSFloaterPoser::onMouseLeaveSavePoseBtn()
{
- if (hasString("icon_save_button"))
- mSavePosesBtn->setImageOverlay(getString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign());
+ mSavePosesBtn->setImageOverlay(tryGetString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign());
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
@@ -523,6 +522,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
{
std::string bone_name = pj.jointName();
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
+ bool jointRotLocked = mPoserAnimator.getRotationIsWorldLocked(avatar, pj);
record[bone_name] = bone_name;
record[bone_name]["enabled"] = posingThisJoint;
@@ -538,9 +538,10 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
continue;
record[bone_name]["jointBaseRotationIsZero"] = baseRotationIsZero;
- record[bone_name]["rotation"] = rotation.getValue();
- record[bone_name]["position"] = position.getValue();
- record[bone_name]["scale"] = scale.getValue();
+ record[bone_name]["rotation"] = rotation.getValue();
+ record[bone_name]["position"] = position.getValue();
+ record[bone_name]["scale"] = scale.getValue();
+ record[bone_name]["worldLocked"] = jointRotLocked;
}
std::string fullSavePath =
@@ -691,7 +692,8 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName)
if (!poserJoint)
return;
- mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, getUiSelectedBoneDeflectionStyle());
+ bool savingToExternal = getSavingToBvh();
+ mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, savingToExternal, getUiSelectedBoneDeflectionStyle());
refreshRotationSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
@@ -723,6 +725,9 @@ void FSFloaterPoser::onClickSymmetrize(const S32 ID)
refreshRotationSlidersAndSpinners();
enableOrDisableRedoAndUndoButton();
refreshTrackpadCursor();
+
+ if (getSavingToBvh())
+ refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onCommitSpinner(const LLUICtrl* spinner, const S32 id)
@@ -964,6 +969,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
mPoserAnimator.loadJointRotation(avatar, poserJoint, true, vec3);
}
}
+
+ addBoldToScrollList(mHandJointsScrollList, avatar);
}
catch ( const std::exception& e )
{
@@ -1041,6 +1048,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
LLQuaternion quat;
bool enabled;
bool setJointBaseRotationToZero;
+ bool worldLocked;
S32 version = 0;
bool startFromZeroRot = true;
@@ -1116,6 +1124,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
vec3.setValue(control_map["scale"]);
mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
}
+
+ worldLocked = control_map.has("worldLocked") ? control_map["worldLocked"].asBoolean() : false;
+ mPoserAnimator.setRotationIsWorldLocked(avatar, *poserJoint, worldLocked);
}
}
}
@@ -1310,8 +1321,8 @@ LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHead
return NULL;
std::string headerValue = "";
- if (isHeaderRow && hasString("icon_category"))
- headerValue = getString("icon_category");
+ if (isHeaderRow)
+ headerValue = tryGetString("icon_category");
std::string jointValue = jointName;
std::string parameterName = (isHeaderRow ? XML_LIST_HEADER_STRING_PREFIX : XML_LIST_TITLE_STRING_PREFIX) + jointName;
@@ -1404,6 +1415,8 @@ void FSFloaterPoser::onUndoLastChange()
refreshPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
refreshTrackpadCursor();
+ if (getSavingToBvh())
+ refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onSetAvatarToTpose()
@@ -1447,6 +1460,7 @@ void FSFloaterPoser::onResetJoint(const LLSD data)
refreshScaleSlidersAndSpinners();
refreshTrackpadCursor();
enableOrDisableRedoAndUndoButton();
+ refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onRedoLastChange()
@@ -1474,6 +1488,8 @@ void FSFloaterPoser::onRedoLastChange()
refreshTrackpadCursor();
refreshScaleSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
+ if (getSavingToBvh())
+ refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::enableOrDisableRedoAndUndoButton()
@@ -1967,7 +1983,7 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
return;
auto selectedJoints = getUiSelectedPoserJoints();
- bool savingToExternal = getWhetherToResetBaseRotationOnEdit();
+ bool savingToExternal = getSavingToBvh();
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
for (auto item : selectedJoints)
@@ -2081,7 +2097,6 @@ void FSFloaterPoser::onJointTabSelect()
refreshTrackpadCursor();
enableOrDisableRedoAndUndoButton();
refreshScaleSlidersAndSpinners();
- onClickSetBaseRotZero();
}
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const
@@ -2348,8 +2363,7 @@ void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists()
void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff)
{
- if (hasString("SavePoseLabel") && hasString("SaveDiffLabel"))
- setAsSaveDiff ? mSavePosesBtn->setLabel(getString("SaveDiffLabel")) : mSavePosesBtn->setLabel(getString("SavePoseLabel"));
+ setAsSaveDiff ? mSavePosesBtn->setLabel(tryGetString("SaveDiffLabel")) : mSavePosesBtn->setLabel(tryGetString("SavePoseLabel"));
}
void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar)
@@ -2361,32 +2375,56 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava
return;
std::string iconValue = "";
- bool considerExternalFormatSaving = getWhetherToResetBaseRotationOnEdit();
-
- if (considerExternalFormatSaving && hasString("icon_rotation_is_own_work"))
- iconValue = getString("icon_rotation_is_own_work");
+ bool considerExternalFormatSaving = getSavingToBvh();
for (auto listItem : list->getAllData())
{
- FSPoserAnimator::FSPoserJoint *userData = static_cast(listItem->getUserdata());
- if (!userData)
+ FSPoserAnimator::FSPoserJoint *poserJoint = static_cast(listItem->getUserdata());
+ if (!poserJoint)
continue;
- if (considerExternalFormatSaving)
- {
- if (mPoserAnimator.baseRotationIsZero(avatar, *userData))
- ((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue(iconValue);
- else
- ((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue("");
- }
+ ((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint));
- if (mPoserAnimator.isPosingAvatarJoint(avatar, *userData))
+ if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
}
}
+std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint)
+{
+ if (!avatar)
+ return "";
+
+ if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint))
+ return tryGetString("icon_rotation_is_world_locked");
+
+ if (!getSavingToBvh())
+ return "";
+
+ if (joint.boneType() == COL_VOLUMES)
+ return tryGetString("icon_rotation_does_not_export");
+
+ if (mPoserAnimator.userSetBaseRotationToZero(avatar, joint))
+ {
+ if (mPoserAnimator.exportRotationWillLockJoint(avatar, joint))
+ return tryGetString("icon_rotation_bvh_locked_edited");
+ else
+ return tryGetString("icon_rotation_bvh_locked_unedited");
+ }
+ else
+ return tryGetString("icon_rotation_bvh_unlocked");
+}
+
+std::string FSFloaterPoser::tryGetString(std::string name)
+{
+ if (name.empty())
+ return "";
+
+ return hasString(name) ? getString(name) : "";
+}
+
bool FSFloaterPoser::savePoseToBvh(LLVOAvatar* avatar, const std::string& poseFileName)
{
if (poseFileName.empty())
@@ -2552,7 +2590,9 @@ void FSFloaterPoser::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar,
if (!joint)
return;
- auto rotation = mPoserAnimator.getJointRotation(avatar, *joint, SWAP_NOTHING, NEGATE_NOTHING);
+ bool lockPelvisJoint = gSavedSettings.getBOOL(POSER_UNLOCKPELVISINBVH_SAVE_KEY);
+
+ auto rotation = mPoserAnimator.getJointExportRotation(avatar, *joint, !lockPelvisJoint);
auto position = mPoserAnimator.getJointPosition(avatar, *joint);
switch (joint->boneType())
@@ -2644,12 +2684,39 @@ S32 FSFloaterPoser::getBvhJointNegation(const std::string& jointName) const
return result;
}
-bool FSFloaterPoser::getWhetherToResetBaseRotationOnEdit() { return gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); }
-
-void FSFloaterPoser::onClickSetBaseRotZero() { mAlsoSaveBvhCbx->setEnabled(getWhetherToResetBaseRotationOnEdit()); }
-
bool FSFloaterPoser::getSavingToBvh()
{
- return getWhetherToResetBaseRotationOnEdit() && gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY);
+ return gSavedSettings.getBOOL(POSER_SAVEEXTERNALFORMAT_SAVE_KEY);
}
+void FSFloaterPoser::onClickSavingToBvh()
+{
+ mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
+ refreshTextHighlightingOnJointScrollLists();
+}
+
+void FSFloaterPoser::onClickLockWorldRotBtn()
+{
+ auto selectedJoints = getUiSelectedPoserJoints();
+ if (selectedJoints.size() < 1)
+ return;
+
+ LLVOAvatar* avatar = getUiSelectedAvatar();
+ if (!avatar)
+ return;
+
+ if (!mPoserAnimator.isPosingAvatar(avatar))
+ return;
+
+ for (auto item : selectedJoints)
+ {
+ bool currentlyPosingJoint = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
+ if (!currentlyPosingJoint)
+ continue;
+
+ bool newLockState = !mPoserAnimator.getRotationIsWorldLocked(avatar, *item);
+ mPoserAnimator.setRotationIsWorldLocked(avatar, *item, newLockState);
+ }
+
+ refreshTextHighlightingOnJointScrollLists();
+}
diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h
index 3603ca659b..69c96a769c 100644
--- a/indra/newview/fsfloaterposer.h
+++ b/indra/newview/fsfloaterposer.h
@@ -264,10 +264,11 @@ public:
void onClickLoadLeftHandPose();
void onClickLoadRightHandPose();
void onClickLoadHandPose(bool isRightHand);
- void onClickSetBaseRotZero();
+ void onClickSavingToBvh();
void onCommitSpinner(const LLUICtrl* spinner, const S32 ID);
void onCommitSlider(const LLUICtrl* slider, const S32 id);
void onClickSymmetrize(const S32 ID);
+ void onClickLockWorldRotBtn();
// UI Refreshments
void refreshRotationSlidersAndSpinners();
@@ -351,12 +352,19 @@ public:
void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar);
///
- /// Gets whether the user wishes to reset the base-rotation to zero when they start editing a joint.
+ /// Gets a string for a joint on a scroll-list.
///
- ///
- /// If a joint has a base-rotation of zero, the rotation then appears to be the user's work and qualifies to save to a re-importable format.
- ///
- bool getWhetherToResetBaseRotationOnEdit();
+ /// The avatar owning the supplied joint.
+ /// The joint to query.
+ /// A string naming an icon to present with the joint.
+ std::string getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint);
+
+ ///
+ /// Tries to get the named string from the XUI.
+ ///
+ /// The name of the string.
+ /// The named string, if it exists, otherwise an empty string.
+ std::string tryGetString(std::string name);
///
/// Gets the name of an item from the supplied object ID.
@@ -483,6 +491,7 @@ public:
LLButton* mFlipJointBtn{ nullptr };
LLButton* mRecaptureBtn{ nullptr };
LLButton* mTogglePosingBonesBtn{ nullptr };
+ LLButton* mToggleLockWorldRotBtn{ nullptr };
LLButton* mToggleMirrorRotationBtn{ nullptr };
LLButton* mToggleSympatheticRotationBtn{ nullptr };
LLButton* mToggleDeltaModeBtn{ nullptr };
@@ -503,8 +512,8 @@ public:
LLPanel* mCollisionVolumesPnl{ nullptr };
LLPanel* mPosesLoadSavePnl{ nullptr };
- LLCheckBoxCtrl* mResetBaseRotCbx{ nullptr };
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr };
+ LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr };
LLUICtrl* mTrackpadSensitivitySpnr{ nullptr };
LLUICtrl* mYawSpnr{ nullptr };
diff --git a/indra/newview/fsjointpose.cpp b/indra/newview/fsjointpose.cpp
index 608e737aba..e1acbce4d0 100644
--- a/indra/newview/fsjointpose.cpp
+++ b/indra/newview/fsjointpose.cpp
@@ -57,9 +57,13 @@ void FSJointPose::setPublicPosition(const LLVector3& pos)
mCurrentState.mPosition.set(pos);
}
-void FSJointPose::setPublicRotation(const LLQuaternion& rot)
+void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
{
addStateToUndo(FSJointState(mCurrentState));
+
+ if (zeroBase)
+ zeroBaseRotation(true);
+
mCurrentState.mRotation.set(rot);
}
@@ -79,6 +83,12 @@ void FSJointPose::redoLastChange()
mCurrentState = redoLastStateChange(FSJointState(mCurrentState));
}
+void FSJointPose::resetJoint()
+{
+ addStateToUndo(FSJointState(mCurrentState));
+ mCurrentState.resetJoint();
+}
+
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
{
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState;
@@ -125,7 +135,7 @@ FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToS
mUndoneJointStatesIndex -= 1;
mUndoneJointStatesIndex = llclamp(mUndoneJointStatesIndex, 0, mLastSetJointStates.size() - 1);
- auto result = mLastSetJointStates.at(mUndoneJointStatesIndex);
+ FSJointState result = mLastSetJointStates.at(mUndoneJointStatesIndex);
if (mUndoneJointStatesIndex == 0)
mLastSetJointStates.pop_front();
@@ -145,14 +155,14 @@ void FSJointPose::recaptureJoint()
mCurrentState = FSJointState(joint);
}
-void FSJointPose::recaptureJointAsDelta()
+LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
- return;
+ return LLQuaternion::DEFAULT;
addStateToUndo(FSJointState(mCurrentState));
- mCurrentState.updateFromJoint(joint);
+ return mCurrentState.updateFromJoint(joint, zeroBase);
}
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
@@ -198,15 +208,13 @@ void FSJointPose::reflectRotation()
mCurrentState.reflectRotation();
}
-void FSJointPose::zeroBaseRotation()
+void FSJointPose::zeroBaseRotation(bool lockInBvh)
{
if (mIsCollisionVolume)
return;
- if (!isBaseRotationZero())
- purgeUndoQueue();
-
mCurrentState.zeroBaseRotation();
+ mCurrentState.mUserSpecifiedBaseZero = lockInBvh;
}
bool FSJointPose::isBaseRotationZero() const
@@ -219,10 +227,37 @@ bool FSJointPose::isBaseRotationZero() const
void FSJointPose::purgeUndoQueue()
{
+ if (mIsCollisionVolume)
+ return;
+
mUndoneJointStatesIndex = 0;
mLastSetJointStates.clear();
}
+bool FSJointPose::userHasSetBaseRotationToZero() const
+{
+ if (mIsCollisionVolume)
+ return false;
+
+ return mCurrentState.mUserSpecifiedBaseZero;
+}
+
+bool FSJointPose::getWorldRotationLockState() const
+{
+ if (mIsCollisionVolume)
+ return false;
+
+ return mCurrentState.mRotationIsWorldLocked;
+}
+
+void FSJointPose::setWorldRotationLockState(bool newState)
+{
+ if (mIsCollisionVolume)
+ return;
+
+ mCurrentState.mRotationIsWorldLocked = newState;
+}
+
bool FSJointPose::canPerformUndo() const
{
switch (mLastSetJointStates.size())
diff --git a/indra/newview/fsjointpose.h b/indra/newview/fsjointpose.h
index 05c96475ff..e4b91f29b6 100644
--- a/indra/newview/fsjointpose.h
+++ b/indra/newview/fsjointpose.h
@@ -77,6 +77,11 @@ class FSJointPose
///
void redoLastChange();
+ ///
+ /// Resets the joint to its conditions when posing started.
+ ///
+ void resetJoint();
+
///
/// Gets the 'public' rotation of the joint.
///
@@ -85,12 +90,14 @@ class FSJointPose
///
/// Sets the 'public' rotation of the joint.
///
+ /// Whether to zero the base rotation on setting the supplied rotation.
+ /// The change in rotation to apply.
///
/// 'Public rotation' is the amount of rotation the user has added to the initial state.
/// Public rotation is what a user may save to an external format (such as BVH).
/// This distinguishes 'private' rotation, which is the state inherited from something like a pose in-world.
///
- void setPublicRotation(const LLQuaternion& rot);
+ void setPublicRotation(bool zeroBase, const LLQuaternion& rot);
///
/// Reflects the base and delta rotation of the represented joint left-right.
@@ -100,7 +107,8 @@ class FSJointPose
///
/// Sets the private rotation of the represented joint to zero.
///
- void zeroBaseRotation();
+ /// Whether the joint should be locked if exported to BVH.
+ void zeroBaseRotation(bool lockInBvh);
///
/// Queries whether the represented joint is zero.
@@ -153,13 +161,33 @@ class FSJointPose
///
/// Recalculates the delta reltive to the base for a new rotation.
///
- void recaptureJointAsDelta();
+ /// Whether to zero the base rotation on setting the supplied rotation.
+ /// The rotation of the public difference between before and after recapture.
+ LLQuaternion recaptureJointAsDelta(bool zeroBase);
///
/// Clears the undo/redo deque.
///
void purgeUndoQueue();
+ ///
+ /// Gets whether the user has specified the base rotation of a joint to be zero.
+ ///
+ /// True if the user performed some action to specify zero rotation as the base, otherwise false.
+ bool userHasSetBaseRotationToZero() const;
+
+ ///
+ /// Gets whether the rotation of a joint has been 'locked' so that its world rotation can remain constant while parent joints change.
+ ///
+ /// True if the joint is rotationally locked to the world, otherwise false.
+ bool getWorldRotationLockState() const;
+
+ ///
+ /// Sets whether the world-rotation of a joint has been 'locked' so that as its parent joints change rotation or position, this joint keeps a constant world rotation.
+ ///
+ /// The new state for the world-rotation lock.
+ void setWorldRotationLockState(bool newState);
+
///
/// Reverts the position/rotation/scale to their values when the animation begun.
/// This treatment is required for certain joints, particularly Collision Volumes and those bones not commonly animated by an AO.
@@ -180,22 +208,16 @@ class FSJointPose
public:
FSJointState(LLJoint* joint)
{
+ mStartingRotation.set(joint->getRotation());
mBaseRotation.set(joint->getRotation());
mBasePosition.set(joint->getPosition());
mBaseScale.set(joint->getScale());
}
FSJointState() = default;
- LLQuaternion mDeltaRotation;
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
- void updateRotation(const LLQuaternion& newRotation)
- {
- auto inv_base = mBaseRotation;
- inv_base.conjugate();
- mDeltaRotation = newRotation * inv_base;
- };
void reflectRotation()
{
@@ -209,10 +231,21 @@ class FSJointPose
{
mBaseRotation.set(otherState.mBaseRotation);
mRotation.set(otherState.mRotation);
+ mUserSpecifiedBaseZero = otherState.mUserSpecifiedBaseZero;
}
bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; }
+ void resetJoint()
+ {
+ mUserSpecifiedBaseZero = false;
+ mRotationIsWorldLocked = false;
+ mBaseRotation.set(mStartingRotation);
+ mRotation.set(LLQuaternion::DEFAULT);
+ mPosition.setZero();
+ mScale.setZero();
+ }
+
void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; }
void revertJointToBase(LLJoint* joint) const
@@ -225,21 +258,33 @@ class FSJointPose
joint->setScale(mBaseScale);
}
- void updateFromJoint(LLJoint* joint)
+ LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase)
{
if (!joint)
- return;
+ return LLQuaternion::DEFAULT;
+ LLQuaternion initalPublicRot = mRotation;
LLQuaternion invRot = mBaseRotation;
invRot.conjugate();
- mRotation = joint->getRotation() * invRot;
+ LLQuaternion newPublicRot = joint->getRotation() * invRot;
+
+ if (zeroBase)
+ {
+ mUserSpecifiedBaseZero = zeroBase;
+ zeroBaseRotation();
+ }
+
+ mRotation.set(newPublicRot);
mPosition.set(joint->getPosition() - mBasePosition);
mScale.set(joint->getScale() - mBaseScale);
+
+ return newPublicRot *= ~initalPublicRot;
}
private:
FSJointState(FSJointState* state)
{
+ mStartingRotation.set(state->mStartingRotation);
mBaseRotation.set(state->mBaseRotation);
mBasePosition.set(state->mBasePosition);
mBaseScale.set(state->mBaseScale);
@@ -247,14 +292,29 @@ class FSJointPose
mRotation.set(state->mRotation);
mPosition.set(state->mPosition);
mScale.set(state->mScale);
+ mUserSpecifiedBaseZero = state->mUserSpecifiedBaseZero;
+ mRotationIsWorldLocked = state->mRotationIsWorldLocked;
}
public:
LLQuaternion mRotation;
LLVector3 mPosition;
LLVector3 mScale;
+ bool mRotationIsWorldLocked = false;
+
+ ///
+ /// A value indicating whether the user has explicitly set the base rotation to zero.
+ ///
+ ///
+ /// The base-rotation, representing any 'current animation' state when posing starts, may become zero for several reasons.
+ /// Loading a Pose, editing a rotation intended to save to BVH, or setting to 'T-Pose' being examples.
+ /// If a user intends on creating a BVH, zero-rotation has a special meaning upon upload: the joint is free (is not animated by that BVH).
+ /// This value represents the explicit intent to have that joint be 'free' in BVH (which is sometimes undesireable).
+ ///
+ bool mUserSpecifiedBaseZero = false;
private:
+ LLQuaternion mStartingRotation;
LLQuaternion mBaseRotation;
LLVector3 mBasePosition;
LLVector3 mBaseScale;
diff --git a/indra/newview/fspanelface.cpp b/indra/newview/fspanelface.cpp
index fd4c8c76a2..5ec5738064 100644
--- a/indra/newview/fspanelface.cpp
+++ b/indra/newview/fspanelface.cpp
@@ -2744,7 +2744,7 @@ void FSPanelFace::refreshMedia()
} func;
// check if all faces have media (or, all don't have media)
- LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue(&func, bool_has_media);
+ bool identical_has_media_info = selected_objects->getSelectedTEValue(&func, bool_has_media);
const LLMediaEntry default_media_data;
@@ -2766,7 +2766,8 @@ void FSPanelFace::refreshMedia()
} func_media_data(default_media_data);
LLMediaEntry media_data_get;
- LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
+ bool multiple_media = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
+ bool multiple_valid_media = false;
std::string multi_media_info_str = LLTrans::getString("Multiple Media");
std::string media_title = "";
@@ -2777,13 +2778,13 @@ void FSPanelFace::refreshMedia()
mBtnAddMedia->setEnabled(editable);
// IF all the faces have media (or all don't have media)
- if (LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo)
+ if (identical_has_media_info)
{
// TODO: get media title and set it.
mTitleMediaText->clear();
// if identical is set, all faces are same (whether all empty or has the same media)
- if (!(LLFloaterMediaSettings::getInstance()->mMultipleMedia))
+ if (!multiple_media)
{
// media data is valid
if (media_data_get != default_media_data)
@@ -2805,9 +2806,9 @@ void FSPanelFace::refreshMedia()
else // not all faces have media but at least one does
{
// selected faces have not identical value
- LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
+ multiple_valid_media = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
- if (LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
+ if (multiple_valid_media)
{
media_title = multi_media_info_str;
}
@@ -2844,7 +2845,7 @@ void FSPanelFace::refreshMedia()
// load values for media settings
updateMediaSettings();
- LLFloaterMediaSettings::initValues(mMediaSettings, editable);
+ LLFloaterMediaSettings::initValues(mMediaSettings, editable, identical_has_media_info, multiple_media, multiple_valid_media);
}
void FSPanelFace::unloadMedia()
diff --git a/indra/newview/fsposeranimator.cpp b/indra/newview/fsposeranimator.cpp
index 62d9030c85..ba91cf356f 100644
--- a/indra/newview/fsposeranimator.cpp
+++ b/indra/newview/fsposeranimator.cpp
@@ -95,6 +95,7 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
jointPose->undoLastChange();
+ undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
if (style == NONE || style == DELTAMODE)
return;
@@ -104,6 +105,10 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
oppositeJointPose->undoLastChange();
+
+ auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
+ if (oppositePoserJoint)
+ undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, false);
}
void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@@ -122,9 +127,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint,
if (!jointPose)
return;
- jointPose->setPublicRotation(LLQuaternion());
- jointPose->setPublicPosition(LLVector3());
- jointPose->setPublicScale(LLVector3());
+ jointPose->resetJoint();
if (style == NONE || style == DELTAMODE)
return;
@@ -133,9 +136,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint,
if (!oppositeJointPose)
return;
- oppositeJointPose->setPublicRotation(LLQuaternion());
- oppositeJointPose->setPublicPosition(LLVector3());
- oppositeJointPose->setPublicScale(LLVector3());
+ oppositeJointPose->resetJoint();
}
bool FSPoserAnimator::canRedoOrUndoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, bool canUndo)
@@ -177,6 +178,7 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
jointPose->redoLastChange();
+ undoOrRedoWorldLockedDescendants(joint, posingMotion, true);
if (style == NONE || style == DELTAMODE)
return;
@@ -186,6 +188,10 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
oppositeJointPose->redoLastChange();
+
+ auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
+ if (oppositePoserJoint)
+ undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, true);
}
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
@@ -265,7 +271,7 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
}
}
-bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const
+bool FSPoserAnimator::getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
if (!isAvatarSafeToUse(avatar))
return false;
@@ -278,7 +284,67 @@ bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint&
if (!jointPose)
return false;
- return jointPose->isBaseRotationZero();
+ return jointPose->getWorldRotationLockState();
+}
+
+void FSPoserAnimator::setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState)
+{
+ if (!isAvatarSafeToUse(avatar))
+ return;
+
+ FSPosingMotion* posingMotion = getPosingMotion(avatar);
+ if (!posingMotion)
+ return;
+
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
+ if (!jointPose)
+ return;
+
+ jointPose->setWorldRotationLockState(newState);
+}
+
+bool FSPoserAnimator::exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const
+{
+ const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; // this is a guestimate: see BVH loader
+
+ if (!isAvatarSafeToUse(avatar))
+ return false;
+
+ FSPosingMotion* posingMotion = getPosingMotion(avatar);
+ if (!posingMotion)
+ return false;
+
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
+ if (!jointPose)
+ return false;
+
+ if (!jointPose->userHasSetBaseRotationToZero())
+ return false;
+
+ F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f);
+ LLQuaternion rotToExport = jointPose->getPublicRotation();
+
+ F32 x_delta = dist_vec(LLVector3::x_axis * LLQuaternion::DEFAULT, LLVector3::x_axis * rotToExport); // when exporting multiple frames this will need to compare frames.
+ F32 y_delta = dist_vec(LLVector3::y_axis * LLQuaternion::DEFAULT, LLVector3::y_axis * rotToExport);
+ F32 rot_test = x_delta + y_delta;
+
+ return rot_test > rot_threshold;
+}
+
+bool FSPoserAnimator::userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const
+{
+ if (!isAvatarSafeToUse(avatar))
+ return false;
+
+ FSPosingMotion* posingMotion = getPosingMotion(avatar);
+ if (!posingMotion)
+ return false;
+
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
+ if (!jointPose)
+ return false;
+
+ return jointPose->userHasSetBaseRotationToZero();
}
bool FSPoserAnimator::allBaseRotationsAreZero(LLVOAvatar* avatar) const
@@ -307,6 +373,21 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
return;
posingMotion->setAllRotationsToZeroAndClearUndo();
+
+ for (size_t index = 0; index != PoserJoints.size(); ++index)
+ {
+ auto boneType = PoserJoints[index].boneType();
+ bool setBvhToLock = boneType == BODY || boneType == WHOLEAVATAR;
+ if (setBvhToLock)
+ continue; // setAllRotationsToZeroAndClearUndo specified this is the default behaviour
+
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
+ if (!jointPose)
+ continue;
+
+ posingMotion->setJointBvhLock(jointPose, false);
+ }
+
}
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation)
@@ -326,7 +407,8 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
setPosingAvatarJoint(avatar, joint, true);
}
-void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style)
+void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
+ E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
@@ -339,7 +421,9 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return;
- jointPose->recaptureJointAsDelta();
+ LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
+
+ deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
if (style == NONE || style == DELTAMODE)
return;
@@ -365,6 +449,35 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
}
}
+LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const
+{
+ auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING);
+ if (exportRotationWillLockJoint(avatar, joint))
+ return rotation;
+
+ LLVector3 vec3;
+ if (!isAvatarSafeToUse(avatar))
+ return vec3;
+
+ FSPosingMotion* posingMotion = getPosingMotion(avatar);
+ if (!posingMotion)
+ return vec3;
+
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
+ if (!jointPose)
+ return vec3;
+
+ if (!jointPose->userHasSetBaseRotationToZero())
+ return vec3;
+
+ if (lockWholeAvatar && joint.boneType() == WHOLEAVATAR)
+ return LLVector3(DEG_TO_RAD * 0.295f, 0.f, 0.f);
+
+ F32 minimumRotation = DEG_TO_RAD * 0.65f / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f);
+
+ return LLVector3(minimumRotation, 0.f, 0.f);
+}
+
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const
{
LLVector3 vec3;
@@ -399,9 +512,6 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
if (!jointPose)
return;
- if (resetBaseRotationToZero)
- jointPose->zeroBaseRotation();
-
LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation);
LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation);
switch (deflectionStyle)
@@ -409,53 +519,65 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case SYMPATHETIC:
case MIRROR:
if (rotationStyle == DELTAIC_ROT)
- jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
+ jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else
- jointPose->setPublicRotation(absRot);
+ jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
break;
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
- jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
+ jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
break;
case DELTAMODE:
- jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
+ jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
+ deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
case NONE:
default:
if (rotationStyle == DELTAIC_ROT)
- jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
+ jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else
- jointPose->setPublicRotation(absRot);
+ jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
+ deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
}
+ deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
+
+ auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
- LLQuaternion inv_quat;
+ LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
switch (deflectionStyle)
{
case SYMPATHETIC:
oppositeJointPose->cloneRotationFrom(jointPose);
+ if (oppositePoserJoint)
+ deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
break;
case SYMPATHETIC_DELTA:
- oppositeJointPose->setPublicRotation(deltaRot * oppositeJointPose->getPublicRotation());
+ oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation());
+ if (oppositePoserJoint)
+ deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
break;
case MIRROR:
oppositeJointPose->mirrorRotationFrom(jointPose);
+ if (oppositePoserJoint)
+ deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
break;
case MIRROR_DELTA:
- inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
- oppositeJointPose->setPublicRotation(inv_quat * oppositeJointPose->getPublicRotation());
+ oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation());
+ if (oppositePoserJoint)
+ deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
break;
default:
@@ -757,11 +879,10 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
if (!jointPose)
return;
- if (setBaseToZero)
- jointPose->zeroBaseRotation();
+ jointPose->purgeUndoQueue();
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
- jointPose->setPublicRotation(rot);
+ jointPose->setPublicRotation(setBaseToZero, rot);
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
@@ -802,7 +923,7 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
jointPose->setPublicScale(scale);
}
-const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName)
+const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const
{
for (size_t index = 0; index != PoserJoints.size(); ++index)
{
@@ -899,3 +1020,118 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const
return true;
}
+
+int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) const
+{
+ size_t numberOfBvhChildNodes = joint->bvhChildren().size();
+ if (numberOfBvhChildNodes < 1)
+ return depth;
+
+ depth++;
+
+ for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
+ {
+ auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
+ if (!nextJoint)
+ continue;
+
+ depth = llmax(depth, getChildJointDepth(nextJoint, depth));
+ }
+
+ return depth;
+}
+
+void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotationChange)
+{
+ size_t numberOfBvhChildNodes = joint->bvhChildren().size();
+ if (numberOfBvhChildNodes < 1)
+ return;
+
+ FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(joint->jointName());
+ if (!parentJoint)
+ return;
+
+ LLJoint* pJoint = parentJoint->getJointState()->getJoint();
+
+ for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
+ {
+ auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
+ if (!nextJoint)
+ continue;
+
+ deRotateJointOrFirstLockedChild(nextJoint, posingMotion, pJoint->getWorldRotation(), rotationChange);
+ }
+}
+
+void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotatedParentWorldRot, LLQuaternion rotationChange)
+{
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
+ if (!jointPose)
+ return;
+
+ if (jointPose->getWorldRotationLockState())
+ {
+ LLQuaternion worldRotOfThisJoint = jointPose->getJointState()->getJoint()->getWorldRotation();
+ LLQuaternion differenceInWorldRot = worldRotOfThisJoint * ~rotatedParentWorldRot;
+ LLQuaternion rotDiffInChildFrame = differenceInWorldRot * rotationChange * ~differenceInWorldRot;
+ rotDiffInChildFrame.conjugate();
+
+ jointPose->setPublicRotation(false, rotDiffInChildFrame * jointPose->getPublicRotation());
+ return;
+ }
+
+ size_t numberOfBvhChildNodes = joint->bvhChildren().size();
+ if (numberOfBvhChildNodes < 1)
+ return;
+
+ for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
+ {
+ auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
+ if (!nextJoint)
+ continue;
+
+ deRotateJointOrFirstLockedChild(nextJoint, posingMotion, rotatedParentWorldRot, rotationChange);
+ }
+}
+
+void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
+{
+ size_t numberOfBvhChildNodes = joint.bvhChildren().size();
+ if (numberOfBvhChildNodes < 1)
+ return;
+
+ for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
+ {
+ auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
+ if (!nextJoint)
+ continue;
+
+ undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
+ }
+}
+
+void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
+{
+ FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
+ if (!jointPose)
+ return;
+
+ if (jointPose->getWorldRotationLockState())
+ {
+ redo ? jointPose->redoLastChange() : jointPose->undoLastChange();
+ return;
+ }
+
+ size_t numberOfBvhChildNodes = joint.bvhChildren().size();
+ if (numberOfBvhChildNodes < 1)
+ return;
+
+ for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
+ {
+ auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
+ if (!nextJoint)
+ continue;
+
+ undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
+ }
+}
diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h
index 8e34b9ba59..853b15d308 100644
--- a/indra/newview/fsposeranimator.h
+++ b/indra/newview/fsposeranimator.h
@@ -392,7 +392,7 @@ public:
///
/// The name of the joint to match.
/// The matching joint if found, otherwise nullptr
- const FSPoserJoint* getPoserJointByName(const std::string& jointName);
+ const FSPoserJoint* getPoserJointByName(const std::string& jointName) const;
///
/// Tries to start posing the supplied avatar.
@@ -492,6 +492,19 @@ public:
/// The rotation of the requested joint, if determinable, otherwise a default vector.
LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const;
+ ///
+ /// Gets the rotation of a joint for the supplied avatar for export.
+ ///
+ /// The avatar whose joint is being queried.
+ /// The joint to determine the rotation for.
+ /// Whether the whole avatar should be rotation/position locked in the BVH export.
+ /// The rotation of the requested joint for export.
+ ///
+ /// The BVH export format requires some minimal amount of rotation so it animates the joint on upload.
+ /// The WHOLEAVATAR joint (mPelvis) never exports as 'free'.
+ ///
+ LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const;
+
///
/// Sets the rotation of a joint for the supplied avatar.
///
@@ -559,8 +572,9 @@ public:
///
/// The avatar whose joint is to be recaptured.
/// The joint to recapture.
+ /// Whether to set the base rotation to zero on setting the rotation.
/// Any ancilliary action to be taken with the change to be made.
- void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style);
+ void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style);
///
/// Sets all of the joint rotations of the supplied avatar to zero.
@@ -574,7 +588,34 @@ public:
/// The avatar owning the supplied joint.
/// The joint to query.
/// True if the supplied joint has a 'base' rotation of zero (thus user-supplied change only), otherwise false.
- bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
+ bool userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
+
+ ///
+ /// Gets whether the supplied joints position will be set in an export.
+ ///
+ /// The avatar owning the supplied joint.
+ /// The joint to query.
+ /// True if the export will 'lock' the joint, otherwise false.
+ ///
+ /// BVH import leaves a joint 'free' if its rotation is less than something arbitrary.
+ ///
+ bool exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
+
+ ///
+ /// Gets whether the supplied joint for the supplied avatar is rotationally locked to the world.
+ ///
+ /// The avatar owning the supplied joint.
+ /// The joint to query.
+ /// True if the joint is maintaining a fixed-rotation in world, otherwise false.
+ bool getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
+
+ ///
+ /// Sets the world-rotation-lock status for supplied joint for the supplied avatar.
+ ///
+ /// The avatar owning the supplied joint.
+ /// The joint to query.
+ /// The lock state to apply.
+ void setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState);
///
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
@@ -692,6 +733,53 @@ public:
/// True if the avatar is safe to manipulate, otherwise false.
bool isAvatarSafeToUse(LLVOAvatar* avatar) const;
+ ///
+ /// Gets the depth of descendant joints for the supplied joint.
+ ///
+ /// The joint to determine the depth for.
+ /// The depth of the supplied joint.
+ /// The number of generations of descendents the joint has, if none, then zero.
+ int getChildJointDepth(const FSPoserJoint* joint, int depth) const;
+
+ ///
+ /// Derotates the first world-locked child joint to the supplied joint.
+ ///
+ /// The edited joint, whose children may be world-locked.
+ /// The posing motion.
+ /// The rotation the supplied joint was/is being changed by.
+ ///
+ /// There are two ways to resolve this problem: before the rotation is applied in the PosingMotion (the animation) or after.
+ /// If performed after, a feedback loop is created, because you're noting the world-rotation in one frame, then correcting it back to that in another.
+ /// This implementation works by applying an opposing-rotation to the locked child joint which is corrected for the relative world-rotations of parent and child.
+ ///
+ void deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotation);
+
+ ///
+ /// Recursively tests the supplied joint and all its children for their world-locked status, and applies a de-rotation if it is world-locked.
+ ///
+ /// The edited joint, whose children may be world-locked.
+ /// The posing motion.
+ /// The world-rotation of the joint that was edited.
+ /// The rotation the joint was edit is being changed by.
+ void deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion parentWorldRot,
+ LLQuaternion rotation);
+
+ ///
+ /// Performs an undo or redo of an edit to the supplied joints world-locked descendants.
+ ///
+ /// The edited joint, whose children may be world-locked.
+ /// The posing motion.
+ /// Whether to redo the edit, otherwise the edit is undone.
+ void undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
+
+ ///
+ /// Recursively tests the supplied joint and all its children for their world-locked status, and applies an undo or redo if it is world-locked.
+ ///
+ /// The joint which will have the undo or redo performed, if it is world locked.
+ /// The posing motion.
+ /// Whether to redo the edit, otherwise the edit is undone.
+ void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
+
///
/// Maps the avatar's ID to the animation registered to them.
/// Thus we start/stop the same animation, and get/set the same rotations etc.
diff --git a/indra/newview/fsposingmotion.cpp b/indra/newview/fsposingmotion.cpp
index 841ab3a452..15fc7d6bb8 100644
--- a/indra/newview/fsposingmotion.cpp
+++ b/indra/newview/fsposingmotion.cpp
@@ -259,11 +259,16 @@ void FSPosingMotion::setAllRotationsToZeroAndClearUndo()
{
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
- poserJoint_iter->zeroBaseRotation();
- poserJoint_iter->setPublicRotation(LLQuaternion::DEFAULT);
+ poserJoint_iter->purgeUndoQueue();
+ poserJoint_iter->setPublicRotation(true, LLQuaternion::DEFAULT);
}
}
+void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
+{
+ joint->zeroBaseRotation(lockInBvh);
+}
+
bool FSPosingMotion::vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const
{
if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) &&
diff --git a/indra/newview/fsposingmotion.h b/indra/newview/fsposingmotion.h
index 9d862ffbef..7e7b8fd9f4 100644
--- a/indra/newview/fsposingmotion.h
+++ b/indra/newview/fsposingmotion.h
@@ -121,8 +121,17 @@ public:
///
/// Sets all of the non-Collision Volume base-and-delta rotations to zero, and clears the undo/redo queue.
///
+ ///
+ /// By default, sets the joint to lock in BVH export.
+ ///
void setAllRotationsToZeroAndClearUndo();
+ ///
+ /// Sets the BVH export state for the supplied joint.
+ ///
+ /// Whether the joint should be locked if exported to BVH.
+ void setJointBvhLock(FSJointPose* joint, bool lockInBvh);
+
private:
///
/// The axial difference considered close enough to be the same.
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index be36c5e90b..c231443a9e 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -146,6 +146,12 @@ namespace LL
dst.set(src[0], src[1]);
}
+ template<>
+ inline void copyVec3(F32* src, LLVector2& dst)
+ {
+ dst.set(src[0], src[1]);
+ }
+
template<>
inline void copyVec3(F32* src, vec3& dst)
{
@@ -375,12 +381,18 @@ namespace LL
template
inline void copy(Asset& asset, Accessor& accessor, LLStrider& dst)
{
- if (accessor.mBufferView == INVALID_INDEX)
+ if (accessor.mBufferView == INVALID_INDEX
+ || accessor.mBufferView >= asset.mBufferViews.size())
{
LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
return;
}
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
+ if (bufferView.mBuffer >= asset.mBuffers.size())
+ {
+ LL_WARNS("GLTF") << "Invalid buffer view" << LL_ENDL;
+ return;
+ }
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index a3de9060aa..1382783ec5 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 27cfa420cc..c77d1b7493 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -140,8 +140,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;
@@ -242,7 +242,6 @@ private:
LLVector3d mPosGlobal;
};
-
class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation
{
public:
@@ -690,7 +689,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())
{
@@ -716,13 +715,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)
@@ -962,7 +956,6 @@ void LLAgent::movePitch(F32 mag)
}
}
-
// Does this parcel allow you to fly?
bool LLAgent::canFly()
{
@@ -1061,7 +1054,6 @@ void LLAgent::setFlying(bool fly, bool fail_sound)
LLFloaterMove::setFlyingMode(fly);
}
-
// UI based mechanism of setting fly state
//-----------------------------------------------------------------------------
// toggleFlying()
@@ -1309,7 +1301,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
mRegionChangedSignal();
}
-
//-----------------------------------------------------------------------------
// getRegion()
//-----------------------------------------------------------------------------
@@ -1318,7 +1309,6 @@ LLViewerRegion *LLAgent::getRegion() const
return mRegionp;
}
-
LLHost LLAgent::getRegionHost() const
{
if (mRegionp)
@@ -1349,7 +1339,6 @@ bool LLAgent::inPrelude()
return mRegionp && mRegionp->isPrelude();
}
-
std::string LLAgent::getRegionCapability(const std::string &name)
{
if (!mRegionp)
@@ -1358,7 +1347,6 @@ std::string LLAgent::getRegionCapability(const std::string &name)
return mRegionp->getCapability(name);
}
-
//-----------------------------------------------------------------------------
// canManageEstate()
//-----------------------------------------------------------------------------
@@ -1386,7 +1374,6 @@ void LLAgent::sendMessage()
gMessageSystem->sendMessage(mRegionp->getHost());
}
-
//-----------------------------------------------------------------------------
// sendReliableMessage()
//-----------------------------------------------------------------------------
@@ -1420,7 +1407,6 @@ LLVector3 LLAgent::getVelocity() const
}
}
-
//-----------------------------------------------------------------------------
// setPositionAgent()
//-----------------------------------------------------------------------------
@@ -1494,7 +1480,6 @@ const LLVector3 &LLAgent::getPositionAgent()
}
}
-
return mFrameAgent.getOrigin();
}
@@ -1503,7 +1488,6 @@ boost::signals2::connection LLAgent::whenPositionChanged(position_signal_t::slot
return mOnPositionChanged.connect(fn);
}
-
//-----------------------------------------------------------------------------
// getRegionsVisited()
//-----------------------------------------------------------------------------
@@ -1520,7 +1504,6 @@ F64 LLAgent::getDistanceTraveled() const
return mDistanceTraveled;
}
-
//-----------------------------------------------------------------------------
// getPosAgentFromGlobal()
//-----------------------------------------------------------------------------
@@ -1531,7 +1514,6 @@ LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const
return pos_agent;
}
-
//-----------------------------------------------------------------------------
// getPosGlobalFromAgent()
//-----------------------------------------------------------------------------
@@ -1552,7 +1534,6 @@ void LLAgent::sitDown()
setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
}
-
//-----------------------------------------------------------------------------
// resetAxes()
//-----------------------------------------------------------------------------
@@ -1561,7 +1542,6 @@ void LLAgent::resetAxes()
mFrameAgent.resetAxes();
}
-
// Copied from LLCamera::setOriginAndLookAt
// Look_at must be unit vector
//-----------------------------------------------------------------------------
@@ -1590,7 +1570,6 @@ void LLAgent::resetAxes(const LLVector3 &look_at)
mFrameAgent.setAxes(look_at, left, up);
}
-
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@@ -1599,7 +1578,6 @@ void LLAgent::rotate(F32 angle, const LLVector3 &axis)
mFrameAgent.rotate(angle, axis);
}
-
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@@ -1608,7 +1586,6 @@ void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z)
mFrameAgent.rotate(angle, x, y, z);
}
-
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@@ -1617,7 +1594,6 @@ void LLAgent::rotate(const LLMatrix3 &matrix)
mFrameAgent.rotate(matrix);
}
-
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@@ -1626,7 +1602,6 @@ void LLAgent::rotate(const LLQuaternion &quaternion)
mFrameAgent.rotate(quaternion);
}
-
//-----------------------------------------------------------------------------
// getReferenceUpVector()
//-----------------------------------------------------------------------------
@@ -1655,7 +1630,6 @@ LLVector3 LLAgent::getReferenceUpVector()
return up_vector;
}
-
// Radians, positive is forward into ground
//-----------------------------------------------------------------------------
// pitch()
@@ -1699,7 +1673,6 @@ void LLAgent::pitch(F32 angle)
}
}
-
//-----------------------------------------------------------------------------
// roll()
//-----------------------------------------------------------------------------
@@ -1708,7 +1681,6 @@ void LLAgent::roll(F32 angle)
mFrameAgent.roll(angle);
}
-
//-----------------------------------------------------------------------------
// yaw()
//-----------------------------------------------------------------------------
@@ -1720,7 +1692,6 @@ void LLAgent::yaw(F32 angle)
}
}
-
// Returns a quat that represents the rotation of the agent in the absolute frame
//-----------------------------------------------------------------------------
// getQuat()
@@ -1756,7 +1727,6 @@ void LLAgent::setControlFlags(U32 mask)
mControlFlags |= mask;
}
-
//-----------------------------------------------------------------------------
// clearControlFlags()
//-----------------------------------------------------------------------------
@@ -2179,7 +2149,6 @@ void LLAgent::startAutoPilotGlobal(
mAutoPilotNoProgressFrameCount = 0;
}
-
//-----------------------------------------------------------------------------
// setAutoPilotTargetGlobal
//-----------------------------------------------------------------------------
@@ -2233,7 +2202,6 @@ void LLAgent::startFollowPilot(const LLUUID &leader_id, bool allow_flying, F32 s
allow_flying);
}
-
//-----------------------------------------------------------------------------
// stopAutoPilot()
//-----------------------------------------------------------------------------
@@ -2275,7 +2243,6 @@ void LLAgent::stopAutoPilot(bool user_cancel)
}
}
-
// Returns necessary agent pitch and yaw changes, radians.
//-----------------------------------------------------------------------------
// autoPilot()
@@ -2464,7 +2431,6 @@ void LLAgent::autoPilot(F32 *delta_yaw)
}
}
-
//-----------------------------------------------------------------------------
// propagate()
//-----------------------------------------------------------------------------
@@ -2485,18 +2451,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
@@ -2677,7 +2644,6 @@ void LLAgent::clearRenderState(U8 clearstate)
mRenderState &= ~clearstate;
}
-
//-----------------------------------------------------------------------------
// getRenderState()
//-----------------------------------------------------------------------------
@@ -2719,6 +2685,7 @@ void LLAgent::endAnimationUpdateUI()
{
return;
}
+
if (gAgentCamera.getCameraMode() == gAgentCamera.getLastCameraMode())
{
// We're already done endAnimationUpdateUI for this transition.
@@ -2835,7 +2802,6 @@ void LLAgent::endAnimationUpdateUI()
}
//
-
gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
if( gMorphView )
{
@@ -3557,7 +3523,6 @@ void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity)
}
}
-
void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity)
{
U8 maturity = SIM_ACCESS_MIN;
@@ -3633,7 +3598,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())
@@ -3988,7 +3952,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
@@ -5104,7 +5067,6 @@ void LLAgent::onCapabilitiesReceivedAfterTeleport()
check_merchant_status();
}
-
//void LLAgent::teleportRequest(
// const U64& region_handle,
// const LLVector3& pos_local,
@@ -5244,7 +5206,6 @@ void LLAgent::doTeleportViaLure(const LLUUID& lure_id, bool godlike)
}
}
-
// James Cook, July 28, 2005
void LLAgent::teleportCancel()
{
@@ -5555,7 +5516,6 @@ LLAgent::ETeleportState LLAgent::getTeleportState() const
// teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt());
//}
-
void LLAgent::setTeleportState(ETeleportState state)
{
if (mTeleportRequest && (state != TELEPORT_NONE) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed))
@@ -5751,7 +5711,6 @@ void LLAgent::stopFidget()
gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
}
-
void LLAgent::requestEnterGodMode()
{
LLMessageSystem* msg = gMessageSystem;
@@ -5887,7 +5846,6 @@ void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& dire
}
}
-
// Keep this for OpenSim
//void LLAgent::updateAgentUserInfoCoro(std::string capurl, std::string directory_visibility)
void LLAgent::updateAgentUserInfoCoro(std::string capurl, bool im_via_email, std::string directory_visibility)
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 57bbdfeacb..9021d8533e 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -1592,13 +1592,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));
@@ -1606,7 +1605,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
@@ -1625,8 +1624,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;
@@ -2570,7 +2569,8 @@ void LLAgentCamera::changeCameraToFollow(bool animate)
AOEngine::getInstance()->inMouselook(false);
// 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 e904513558..aa24fbc1c2 100644
--- a/indra/newview/llagentcamera.h
+++ b/indra/newview/llagentcamera.h
@@ -121,11 +121,8 @@ private:
// Preset
//--------------------------------------------------------------------
public:
-// [RLVa:KB] - @setcam family
- /** Determines default camera offset scale depending on the current camera preset */
- ECameraPreset getCameraPreset() const { return mCameraPreset; }
-// [/RLVa:KB]
void switchCameraPreset(ECameraPreset preset);
+ ECameraPreset getCameraPreset() const { return mCameraPreset; }
/** Determines default camera offset depending on the current camera preset */
LLVector3 getCameraOffsetInitial();
// [RLVa:KB] - @setcam_eyeoffsetscale
@@ -177,8 +174,9 @@ public:
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();
@@ -188,7 +186,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;
@@ -320,7 +317,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/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 3feb484135..8f66a660da 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -1230,12 +1230,12 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
{
gAgentAvatarp->setCompositeUpdatesEnabled(true);
- // If we have not yet declouded, we may want to use
+ // If we have not yet loaded core parts, we may want to use
// baked texture UUIDs sent from the first objectUpdate message
- // don't overwrite these. If we have already declouded, we've saved
- // these ids as the last known good textures and can invalidate without
- // re-clouding.
- if (!gAgentAvatarp->getIsCloud())
+ // don't overwrite these. If we have parts already, we've saved
+ // these texture ids as the last known good textures and can
+ // invalidate without having to recloud avatar.
+ if (!gAgentAvatarp->getHasMissingParts())
{
gAgentAvatarp->invalidateAll();
}
diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index bb86839f56..ed91be6990 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -844,7 +844,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 f1b7fdc044..d2bb1325ce 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -868,7 +868,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
// was requested but none was found, create a default asset as a replacement.
// In all other cases, don't do anything.
// For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud
- // due to logic in LLVOAvatarSelf::getIsCloud().
+ // due to logic in LLVOAvatarSelf::getHasMissingParts().
// For non-critical types (tatoo, socks, etc.) the wearable will just be missing.
(requested_by_type[type] > 0) &&
((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT)))
@@ -2152,7 +2152,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
@@ -2182,7 +2182,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;
}
}
@@ -2464,7 +2467,7 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new,
}
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);
@@ -4381,7 +4384,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);
@@ -4489,7 +4492,7 @@ void LLAppearanceMgr::syncCofVersionAndRefreshCoro()
/*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 f3183c2304..57cc2584ab 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -108,7 +108,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 a6e192ffa5..300e835128 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -301,6 +301,16 @@ using namespace LL;
#include "glib.h"
#endif // (LL_LINUX) && LL_GTK
+#ifdef LL_DISCORD
+#define DISCORDPP_IMPLEMENTATION
+#include
+static std::shared_ptr gDiscordClient;
+static uint64_t gDiscordTimestampsStart;
+static std::string gDiscordActivityDetails;
+static int32_t gDiscordPartyCurrentSize;
+static int32_t gDiscordPartyMaxSize;
+#endif
+
const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; // We need this filename defined here.
static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);
@@ -506,6 +516,12 @@ 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();
// Cache frequently hit location
@@ -516,11 +532,20 @@ void idle_afk_check()
// [/RLVa:KB]
// static LLCachedControl afk_timeout(gSavedSettings, "AFKTimeout", 300);
// Explicit conversions just cos.
- //if (afk_timeout() && (current_idle > (F32)afk_timeout()) && !gAgent.getAFK())
- if (static_cast(afk_timeout) && (current_idle > static_cast(afk_timeout)) && ! gAgent.getAFK())
+ //if (afk_timeout() && (current_idle > afk_timeout()) && !gAgent.getAFK())
+ if (static_cast(afk_timeout) && (current_idle > static_cast(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();
+ }
}
}
@@ -1546,6 +1571,13 @@ bool LLAppViewer::frame()
bool LLAppViewer::doFrame()
{
+#ifdef LL_DISCORD
+ {
+ LL_PROFILE_ZONE_NAMED("discord_callbacks");
+ discordpp::RunCallbacks();
+ }
+#endif
+
LL_RECORD_BLOCK_TIME(FTM_FRAME);
{
// and now adjust the visuals from previous frame.
@@ -2185,36 +2217,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();
@@ -2413,6 +2415,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();
@@ -3668,15 +3700,15 @@ bool LLAppViewer::initWindow()
.height(gSavedSettings.getU32("WindowHeight"))
.min_width(gSavedSettings.getU32("MinWindowWidth"))
.min_height(gSavedSettings.getU32("MinWindowHeight"))
-/// Since the 3.6.5 merge, setting fullscreen does terrible bad things on macs like opening
-/// all floaters and menus off the left side of the screen. Let's not do that right now...
-/// Hardcoding full screen OFF until it's fixed. On 10.7+ we have native full screen support anyway.
-#ifndef LL_DARWIN
- .fullscreen(gSavedSettings.getBOOL("FullScreen"))
-#else // !LL_DARWIN
+#ifdef LL_DARWIN
+ // Setting it to true causes black screen with no UI displayed.
+ // Given that it's a DEBUG settings and application goes fullscreen
+ // on mac simply by expanding it, it was decided to not support/use
+ // this setting on mac.
.fullscreen(false)
-#endif // !LL_DARWIN
-//
+#else // LL_DARWIN
+ .fullscreen(gSavedSettings.getBOOL("FullScreen"))
+#endif
.ignore_pixel_depth(ignorePixelDepth)
.first_run(mIsFirstRun);
@@ -6883,3 +6915,180 @@ void LLAppViewer::metricsSend(bool enable_reporting)
gViewerAssetStats->restart();
}
+#ifdef LL_DISCORD
+
+void LLAppViewer::initDiscordSocial()
+{
+ gDiscordPartyCurrentSize = 1;
+ 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;
+ }
+ }
+}
+
+void LLAppViewer::updateDiscordActivity()
+{
+ LL_PROFILE_ZONE_SCOPED;
+ discordpp::Activity activity;
+ activity.SetType(discordpp::ActivityTypes::Playing);
+ discordpp::ActivityTimestamps timestamps;
+ timestamps.SetStart(gDiscordTimestampsStart);
+ activity.SetTimestamps(timestamps);
+
+ if (gAgent.getID() == LLUUID::null)
+ {
+ gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
+ return;
+ }
+
+ static LLCachedControl show_details(gSavedSettings, "ShowDiscordActivityDetails", false);
+ if (show_details)
+ {
+ if (gDiscordActivityDetails.empty())
+ {
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(gAgent.getID(), &av_name);
+ gDiscordActivityDetails = av_name.getUserName();
+ auto displayName = av_name.getDisplayName();
+ if (gDiscordActivityDetails != displayName)
+ gDiscordActivityDetails = displayName + " (" + gDiscordActivityDetails + ")";
+ }
+ activity.SetDetails(gDiscordActivityDetails);
+ }
+
+ 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);
+ }
+
+ gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
+}
+
+void LLAppViewer::updateDiscordPartyCurrentSize(int32_t size)
+{
+ gDiscordPartyCurrentSize = size;
+ updateDiscordActivity();
+}
+
+void LLAppViewer::updateDiscordPartyMaxSize(int32_t size)
+{
+ gDiscordPartyMaxSize = size;
+ updateDiscordActivity();
+}
+
+#endif
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index ebeb058f4d..77de33f5f0 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -252,6 +252,14 @@ public:
// Note: mQuitRequested can be aborted by user.
void outOfMemorySoftQuit();
+#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);
+#endif
+
protected:
virtual bool initWindow(); // Initialize the viewer's window.
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 8d5e26caa1..ba3e900580 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -514,6 +514,7 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
// *FIX: global
gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
+ gIconSmallResource = MAKEINTRESOURCE(IDI_LL_ICON_SMALL);
LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(ll_convert_wide_to_string(pCmdLine).c_str());
diff --git a/indra/newview/llautoreplace.cpp b/indra/newview/llautoreplace.cpp
index 79b6e00559..239c058a35 100644
--- a/indra/newview/llautoreplace.cpp
+++ b/indra/newview/llautoreplace.cpp
@@ -537,11 +537,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 5b6729008f..2b8d1f8ec5 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -989,7 +989,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/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
index a051060e52..26f3b6a21b 100644
--- a/indra/newview/llcontrolavatar.cpp
+++ b/indra/newview/llcontrolavatar.cpp
@@ -101,6 +101,12 @@ LLVOAvatar *LLControlAvatar::getAttachedAvatar()
void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
{
+ static LLCachedControl anim_max_legal_offset(gSavedSettings, "AnimatedObjectsMaxLegalOffset", MAX_LEGAL_OFFSET);
+ F32 max_legal_offset = llmax(anim_max_legal_offset(), 0.f);
+
+ static LLCachedControl anim_max_legal_size(gSavedSettings, "AnimatedObjectsMaxLegalSize", MAX_LEGAL_SIZE);
+ F32 max_legal_size = llmax(anim_max_legal_size(), 1.f);
+
new_pos_fixup = LLVector3();
new_scale_fixup = 1.0f;
LLVector3 vol_pos = mRootVolp->getRenderPosition();
@@ -125,14 +131,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;
@@ -140,11 +146,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;
}
}
@@ -227,7 +233,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 ff642e4e21..4c8939d4e1 100644
--- a/indra/newview/llconversationlog.cpp
+++ b/indra/newview/llconversationlog.cpp
@@ -691,7 +691,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 5cf712074e..6841f2f6ba 100644
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -302,7 +302,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)
{
@@ -331,6 +331,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic
void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant)
{
+ LLPointer holder(participant);
removeChild(participant);
mNeedsRefresh = true;
updateName(participant);
@@ -362,15 +363,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);
@@ -385,7 +381,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;
@@ -495,7 +491,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))
{
@@ -519,7 +515,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 6ae5a9dd4d..d91a67ab6d 100644
--- a/indra/newview/llconversationmodel.h
+++ b/indra/newview/llconversationmodel.h
@@ -82,6 +82,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/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index b81d74cc9f..e2f2bdf5fc 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -1455,6 +1455,20 @@ static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker)
LLTrans::getString("collada_files") + " (*.dae)");
}
+// migrate to GLTF support
+static std::string add_model_filter_to_gtkchooser(GtkWindow *picker)
+{
+// "Model files (*.dae, *.gltf, *.glb)"
+ GtkFileFilter *gfilter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(gfilter, "*.dae");
+ gtk_file_filter_add_pattern(gfilter, "*.gltf");
+ gtk_file_filter_add_pattern(gfilter, "*.glb");
+ std::string filtername = LLTrans::getString("model_files") + " (*.dae; *.gltf; *.glb)";
+ add_common_filters_to_gtkchooser(gfilter, picker, filtername);
+ return filtername;
+}
+//
+
static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
{
GtkFileFilter *gfilter = gtk_file_filter_new();
@@ -1676,7 +1690,7 @@ bool LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
filtername = dead_code_should_blow_up_here(picker);
break;
case FFLOAD_COLLADA:
- filtername = add_collada_filter_to_gtkchooser(picker);
+ filtername = add_model_filter_to_gtkchooser(picker);
break;
case FFLOAD_IMAGE:
filtername = add_imageload_filter_to_gtkchooser(picker);
@@ -1936,6 +1950,9 @@ bool LLFilePicker::openFileDialog( int32_t filter, bool blocking, EType aType )
file_dialog_filter = "*.raw";
break;
case FFLOAD_MODEL:
+ file_type = "model_files";
+ file_dialog_filter = "*.{dae,gltf,glb}";
+ break;
case FFLOAD_COLLADA:
file_type = "collada_files";
file_dialog_filter = "*.dae";
diff --git a/indra/newview/llfloaterautoreplacesettings.cpp b/indra/newview/llfloaterautoreplacesettings.cpp
index 792640a030..b90476a5b1 100644
--- a/indra/newview/llfloaterautoreplacesettings.cpp
+++ b/indra/newview/llfloaterautoreplacesettings.cpp
@@ -420,17 +420,15 @@ bool LLFloaterAutoReplaceSettings::callbackNewListName(const LLSD& notification,
{
LL_DEBUGS("AutoReplace")<<"called"< FIRE-31256: Add Cancel button for new auto-replace list dialog
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0)
- {
- return false;
- }
- //
-
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);
@@ -516,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 fc12854f99..b517b2f4a5 100644
--- a/indra/newview/llfloaterbvhpreview.cpp
+++ b/indra/newview/llfloaterbvhpreview.cpp
@@ -128,8 +128,8 @@ std::string STATUS[] =
//-----------------------------------------------------------------------------
// LLFloaterBvhPreview()
//-----------------------------------------------------------------------------
-LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename) :
- LLFloaterNameDesc(filename)
+LLFloaterBvhPreview::LLFloaterBvhPreview(const LLSD& args) :
+ LLFloaterNameDesc(args)
{
mLastMouseX = 0;
mLastMouseY = 0;
@@ -1447,7 +1447,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 5f99d962a3..9e6af81d78 100644
--- a/indra/newview/llfloaterbvhpreview.h
+++ b/indra/newview/llfloaterbvhpreview.h
@@ -77,7 +77,7 @@ class LLFloaterBvhPreview : public LLFloaterNameDesc
friend class LLPreviewAnimation;
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 d30e533062..eef0b7e1a4 100644
--- a/indra/newview/llfloaterchatmentionpicker.cpp
+++ b/indra/newview/llfloaterchatmentionpicker.cpp
@@ -94,7 +94,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 b8113c5adf..c4d4eae860 100644
--- a/indra/newview/llfloaterimagepreview.cpp
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -32,6 +32,7 @@
#include "llimagetga.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
+#include "llimagej2c.h"
#include "llagent.h"
#include "llagentbenefits.h"
@@ -43,6 +44,10 @@
#include "llrender.h"
#include "llface.h"
#include "llfocusmgr.h"
+#include "llfilesystem.h"
+#include "llfloaterperms.h"
+#include "llnotificationsutil.h"
+#include "llstatusbar.h" // can_afford_transaction()
#include "lltextbox.h"
#include "lltoolmgr.h"
#include "llui.h"
@@ -52,6 +57,7 @@
#include "llvoavatar.h"
#include "pipeline.h"
#include "lluictrlfactory.h"
+#include "llviewermenufile.h" // upload_new_resource()
#include "llviewershadermgr.h"
#include "llviewertexturelist.h"
#include "llviewercontrol.h"
@@ -85,8 +91,8 @@ constexpr F32 ALPHA_EMPTY_THRESHOLD_RATIO = 0.999f;
//-----------------------------------------------------------------------------
// LLFloaterImagePreview()
//-----------------------------------------------------------------------------
-LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename) :
- LLFloaterNameDesc(filename),
+LLFloaterImagePreview::LLFloaterImagePreview(const LLSD& args) :
+ LLFloaterNameDesc(args),
mAvatarPreview(NULL),
mSculptedPreview(NULL),
@@ -236,7 +242,7 @@ bool LLFloaterImagePreview::postBuild()
}
// detect and strip empty alpha layers from images on upload
- // getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this));
+ // getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnOK, this));
return true;
}
@@ -392,6 +398,61 @@ void LLFloaterImagePreview::clearAllPreviewTextures()
}
}
+//-----------------------------------------------------------------------------
+// onBtnOK()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::onBtnOK()
+{
+ getChildView("ok_btn")->setEnabled(false); // don't allow inadvertent extra uploads
+
+ S32 expected_upload_cost = getExpectedUploadCost();
+ if (can_afford_transaction(expected_upload_cost))
+ {
+ LL_INFOS() << "saving texture: " << mRawImagep->getWidth() << "x" << mRawImagep->getHeight() << LL_ENDL;
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer formatted = new LLImageJ2C;
+
+ if (formatted->encode(mRawImagep, 0.0f))
+ {
+ LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
+ fmt_file.write(formatted->getData(), formatted->getDataSize());
+
+ LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo(
+ tid, LLAssetType::AT_TEXTURE,
+ getChild("name_form")->getValue().asString(),
+ getChild("description_form")->getValue().asString(),
+ 0,
+ LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost
+ ));
+
+ upload_new_resource(assetUploadInfo);
+ }
+ else
+ {
+ LLSD args;
+ args["REASON"] = LLImage::getLastThreadError();
+ LLNotificationsUtil::add("ErrorEncodingImage", args);
+ LL_WARNS() << "Error encoding image" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LLSD args;
+ args["COST"] = llformat("%d", expected_upload_cost);
+ LLNotificationsUtil::add("ErrorCannotAffordUpload", args);
+ }
+
+ closeFloater(false);
+}
+
//-----------------------------------------------------------------------------
// draw()
//-----------------------------------------------------------------------------
@@ -545,16 +606,15 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
return false;
}
- S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
- S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
+ // 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() > max_width) || (image_info.getHeight() > max_height))
+ if (image_info.getWidth() * image_info.getHeight() > MAX_IMAGE_AREA)
{
LLStringUtil::format_map_t args;
- args["WIDTH"] = llformat("%d", max_width);
- args["HEIGHT"] = llformat("%d", max_height);
+ args["PIXELS"] = llformat("%dM", (S32)(MAX_IMAGE_AREA / 1000000));
- mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
+ mImageLoadError = LLTrans::getString("texture_load_area_error", args);
return false;
}
@@ -580,6 +640,46 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
+ // Downscale images to fit the max_texture_dimensions_*
+ S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
+ S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
+
+ S32 orig_width = raw_image->getWidth();
+ S32 orig_height = raw_image->getHeight();
+
+ if (orig_width > max_width || orig_height > max_height)
+ {
+ // Calculate scale factors
+ F32 width_scale = (F32)max_width / (F32)orig_width;
+ F32 height_scale = (F32)max_height / (F32)orig_height;
+ F32 scale = llmin(width_scale, height_scale);
+
+ // Calculate new dimensions, preserving aspect ratio
+ S32 new_width = LLImageRaw::contractDimToPowerOfTwo(
+ llclamp((S32)llroundf(orig_width * scale), 4, max_width)
+ );
+ S32 new_height = LLImageRaw::contractDimToPowerOfTwo(
+ llclamp((S32)llroundf(orig_height * scale), 4, max_height)
+ );
+
+ if (!raw_image->scale(new_width, new_height))
+ {
+ LL_WARNS() << "Failed to scale image from "
+ << orig_width << "x" << orig_height
+ << " to " << new_width << "x" << new_height << LL_ENDL;
+ return false;
+ }
+
+ // Inform the resident about the resized image
+ LLSD subs;
+ subs["[ORIGINAL_WIDTH]"] = orig_width;
+ subs["[ORIGINAL_HEIGHT]"] = orig_height;
+ subs["[NEW_WIDTH]"] = new_width;
+ subs["[NEW_HEIGHT]"] = new_height;
+ subs["[MAX_WIDTH]"] = max_width;
+ subs["[MAX_HEIGHT]"] = max_height;
+ LLNotificationsUtil::add("ImageUploadResized", subs);
+ }
raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
mRawImagep = raw_image;
diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h
index 94f65d12f0..195d509c51 100644
--- a/indra/newview/llfloaterimagepreview.h
+++ b/indra/newview/llfloaterimagepreview.h
@@ -111,7 +111,7 @@ protected:
class LLFloaterImagePreview : public LLFloaterNameDesc
{
public:
- LLFloaterImagePreview(const std::string& filename);
+ LLFloaterImagePreview(const LLSD& args);
virtual ~LLFloaterImagePreview();
bool postBuild() override;
@@ -127,6 +127,8 @@ public:
void clearAllPreviewTextures();
+ void onBtnOK();
+
protected:
static void onPreviewTypeCommit(LLUICtrl*,void*);
void draw() override;
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 81b45f0582..75ed348ca0 100644
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -464,7 +464,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
@@ -515,7 +515,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);
@@ -1544,6 +1544,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);
@@ -1886,7 +1890,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 0d1d4953dd..879dc0e008 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -109,26 +109,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
@@ -732,7 +712,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);
@@ -776,27 +756,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);
@@ -862,7 +821,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/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp
index 2496887c9d..81eab52e6c 100644
--- a/indra/newview/llfloatermediasettings.cpp
+++ b/indra/newview/llfloatermediasettings.cpp
@@ -180,8 +180,15 @@ void LLFloaterMediaSettings::onClose(bool app_quitting)
////////////////////////////////////////////////////////////////////////////////
//static
-void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable )
+void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable, bool has_media_info, bool multiple_media, bool multiple_valid_media)
{
+ if (!sInstance)
+ {
+ return;
+ }
+ sInstance->mIdenticalHasMediaInfo = has_media_info;
+ sInstance->mMultipleMedia = multiple_media;
+ sInstance->mMultipleValidMedia = multiple_valid_media;
if (sInstance->hasFocus()) return;
// Clear values
diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h
index 38730ddc98..7ed7ab246f 100644
--- a/indra/newview/llfloatermediasettings.h
+++ b/indra/newview/llfloatermediasettings.h
@@ -48,7 +48,7 @@ public:
static LLFloaterMediaSettings* getInstance();
static bool instanceExists();
static void apply();
- static void initValues( const LLSD& media_settings , bool editable);
+ static void initValues( const LLSD& media_settings , bool editable, bool has_media_info, bool multiple_media, bool multiple_valid_media);
static void clearValues( bool editable);
LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;};
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 857a145557..022bed0925 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -191,7 +191,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));
@@ -426,14 +426,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)
@@ -590,10 +590,13 @@ void LLFloaterModelPreview::onClickCalculateBtn()
mUploadModelUrl.clear();
mModelPhysicsFee.clear();
- gMeshRepo.uploadModel(mModelPreview->mUploadData, mModelPreview->mPreviewScale,
+ lod_sources_map_t lod_sources;
+ fillLODSourceStatistics(lod_sources);
+
+ gMeshRepo.uploadModel(mModelPreview->mUploadData, lod_sources, mModelPreview->mPreviewScale,
childGetValue("upload_textures").asBoolean(),
upload_skinweights, upload_joint_positions, lock_scale_if_joint_position,
- mUploadModelUrl, false,
+ mUploadModelUrl, mDestinationFolderId, false,
getWholeModelFeeObserverHandle());
toggleCalculateButton(false);
@@ -858,7 +861,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
{
@@ -1201,9 +1204,7 @@ void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata)
}
else if (which_mode == cube_mode)
{
- std::string path = gDirUtilp->getAppRODataDir();
- gDirUtilp->append(path, "cube.dae");
- sInstance->loadModel(LLModel::LOD_PHYSICS, path);
+ sInstance->loadModel(LLModel::LOD_PHYSICS, getBoundingBoxCubePath());
}
LLModelPreview *model_preview = sInstance->mModelPreview;
@@ -1500,8 +1501,91 @@ void LLFloaterModelPreview::createSmoothComboBox(LLComboBox* combo_box, float mi
std::string label = (++ilabel == SMOOTH_VALUES_NUMBER) ? "10 (max)" : llformat("%.1d", ilabel);
combo_box->add(label, value, ADD_BOTTOM, true);
}
+}
+std::string get_source_file_format(const std::string& filename)
+{
+ const std::string extension = gDirUtilp->getExtension(filename);
+ if (extension == "gltf"
+ || extension == "glb")
+ {
+ return "gltf";
+ }
+ else if (extension == "dae")
+ {
+ return "dae";
+ }
+ else if (extension == "slm")
+ {
+ return "slm";
+ }
+ else
+ {
+ return "unknown file";
+ }
+}
+std::string LLFloaterModelPreview::getBoundingBoxCubePath()
+{
+ std::string path = gDirUtilp->getAppRODataDir();
+ gDirUtilp->append(path, "cube.dae");
+ return path;
+}
+
+void LLFloaterModelPreview::fillLODSourceStatistics(LLFloaterModelPreview::lod_sources_map_t& lod_sources) const
+{
+ lod_sources.clear();
+
+ // This doesn't nessesarily reflect the actual source of meshes, just user choices,
+ // some meshes could have been matched from different lods, but should be good
+ // enough for statistics.
+ for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
+ {
+ const std::string &lod_string = lod_name[lod];
+ if (mLODMode[lod] == LLModelPreview::USE_LOD_ABOVE)
+ {
+ lod_sources[lod_string] = "lod above";
+ }
+ else if (mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_AUTO
+ || mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_PRECISE
+ || mLODMode[lod] == LLModelPreview::MESH_OPTIMIZER_SLOPPY)
+ {
+ lod_sources[lod_string] = "generated";
+ }
+ else if (mLODMode[lod] == LLModelPreview::LOD_FROM_FILE)
+ {
+ const std::string& file = mModelPreview->mLODFile[lod];
+ lod_sources[lod_string] = get_source_file_format(file);
+ }
+ else
+ {
+ lod_sources[lod_string] = "unknown source";
+ }
+ }
+ if (mModelPreview->mLODFile[LLModel::LOD_PHYSICS].empty())
+ {
+ if (mModelPreview->mPhysicsSearchLOD >= 0 && mModelPreview->mPhysicsSearchLOD <= 3)
+ {
+ lod_sources["physics"] = lod_name[mModelPreview->mPhysicsSearchLOD];
+ }
+ else
+ {
+ lod_sources["physics"] = "none";
+ }
+ }
+ else
+ {
+ const std::string& file = mModelPreview->mLODFile[LLModel::LOD_PHYSICS];
+ const std::string cube = getBoundingBoxCubePath();
+ if (cube != file) // check for "cube.dae"
+ {
+ lod_sources["physics"] = get_source_file_format(file);
+ }
+ else
+ {
+ lod_sources["physics"] = "bounding box";
+ }
+ }
}
//-----------------------------------------------------------------------------
@@ -1839,10 +1923,13 @@ void LLFloaterModelPreview::onUpload(void* user_data)
mp->mModelPreview->saveUploadData(upload_skinweights, upload_joint_positions, lock_scale_if_joint_position);
}
- gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale,
+ lod_sources_map_t lod_sources;
+ mp->fillLODSourceStatistics(lod_sources);
+
+ gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, lod_sources, 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());
}
@@ -1943,7 +2030,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
}
}
-void LLFloaterModelPreview::onLoDSourceCommit(S32 lod, bool refresh_ui)
+void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
{
mModelPreview->updateLodControls(lod);
@@ -1969,12 +2056,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 c67d4df21e..5a1d058817 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);
static void onSuffixStandardSelected(LLUICtrl* ctrl, void* userdata); // mesh loader suffix configuration
static void onSelectUDPhysics(LLUICtrl* ctrl, void* userdata); // custom setter for upload preview settings tab
@@ -210,7 +208,7 @@ private:
void onClickCalculateBtn();
void onJointListSelection();
- void onLoDSourceCommit(S32 lod, bool refresh_ui);
+ void onLoDSourceCommit(S32 lod);
void modelUpdated(bool calculate_visible);
@@ -227,6 +225,11 @@ private:
void createSmoothComboBox(LLComboBox* combo_box, float min, float max);
+ static std::string getBoundingBoxCubePath();
+ typedef std::map lod_sources_map_t;
+ void fillLODSourceStatistics(lod_sources_map_t& lod_sources) const;
+
+ 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 2fb2620348..d9b016f8e2 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -550,6 +550,11 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
mCommitCallbackRegistrar.add("Pref.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
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("ShowDiscordActivityDetails")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity));
+ gSavedSettings.getControl("ShowDiscordActivityState")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity));
+#endif
//
mCommitCallbackRegistrar.add("NACL.AntiSpamUnblock", boost::bind(&LLFloaterPreference::onClickClearSpamList, this));
@@ -759,6 +764,13 @@ bool LLFloaterPreference::postBuild()
getChild("language_combobox")->add("System default", LLSD("default"), ADD_TOP, true);
}
+// Prefer FS-specific Discord implementation
+//#ifndef LL_DISCORD
+// LLPanel* panel = getChild("privacy_preferences_discord");
+// getChild("privacy_tab_container")->removeTabPanel(panel);
+//#endif
+//
+
// [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-06-11 (Catznip-2.6.c) | Added: Catznip-2.6.0c
#ifndef LL_SEND_CRASH_REPORTS
// Hide the crash report tab if crash reporting isn't enabled
@@ -3211,7 +3223,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/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index 258bba5df0..01deaa0984 100644
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -136,7 +136,7 @@ void LLFloaterWebContent::initializeURLHistory()
for(; iter_history != end_history; ++iter_history)
{
std::string url = (*iter_history).asString();
- if(! url.empty())
+ if(! url.empty() && url_list)
url_list->addSimpleElement(url);
}
}
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index 23874b4832..71a3aa0c01 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -187,6 +187,52 @@ public:
};
LLWorldMapHandler gWorldMapHandler;
+// handle secondlife:///app/worldmap_global/{GLOBAL_COORDS} URLs
+class LLWorldMapGlobalHandler : public LLCommandHandler
+{
+public:
+ LLWorldMapGlobalHandler() : LLCommandHandler("worldmap_global", UNTRUSTED_THROTTLE)
+ {}
+
+ virtual bool canHandleUntrusted(
+ const LLSD& params,
+ const LLSD& query_map,
+ LLMediaCtrl* web,
+ const std::string& nav_type)
+ {
+ if (nav_type == NAV_TYPE_CLICKED
+ || nav_type == NAV_TYPE_EXTERNAL)
+ {
+ // NAV_TYPE_EXTERNAL will be throttled
+ return true;
+ }
+
+ return false;
+ }
+
+ bool handle(const LLSD& params,
+ const LLSD& query_map,
+ const std::string& grid,
+ LLMediaCtrl* web)
+ {
+ if (params.size() < 3)
+ {
+ LL_WARNS() << "Correct global coordinates are not provided." << LL_ENDL;
+ return true;
+ }
+
+ LLVector3d parcel_global_pos = LLVector3d(params[0].asInteger(), params[1].asInteger(), params[2].asInteger());
+ LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
+ if (!parcel_global_pos.isExactlyZero() && worldmap_instance)
+ {
+ worldmap_instance->trackLocation(parcel_global_pos);
+ LLFloaterReg::showInstance("world_map", "center");
+ }
+ return true;
+ }
+};
+LLWorldMapGlobalHandler gWorldMapGlobalHandler;
+
// SocialMap handler secondlife:///app/maptrackavatar/id
class LLMapTrackAvatarHandler : public LLCommandHandler
{
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 f074af46e0..de47da022c 100644
--- a/indra/newview/llgltffolderitem.h
+++ b/indra/newview/llgltffolderitem.h
@@ -116,6 +116,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/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp
index 823435daac..4885f966b4 100644
--- a/indra/newview/llhudeffectlookat.cpp
+++ b/indra/newview/llhudeffectlookat.cpp
@@ -37,6 +37,7 @@
#include "lldrawable.h"
#include "llviewerobjectlist.h"
#include "llviewercontrol.h"
+#include "llvoavatarself.h"
#include "llrendersphere.h"
#include "llselectmgr.h"
#include "llglheaders.h"
@@ -463,6 +464,23 @@ bool LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *objec
return false;
}
+ // Prefer custom FS lookat privacy code
+ //static LLCachedControl enable_lookat_hints(gSavedSettings, "EnableLookAtTarget", true);
+ //if (!enable_lookat_hints)
+ //{
+ // // Clear the effect so it doesn't linger around if it gets disabled
+ // if (mTargetType != LOOKAT_TARGET_IDLE)
+ // {
+ // mTargetObject = gAgentAvatarp;
+ // mTargetType = LOOKAT_TARGET_IDLE;
+ // mTargetOffsetGlobal.set(2.f, 0.f, 0.f);
+ // setDuration(3.f);
+ // setNeedsSendToSim(true);
+ // }
+ // return false;
+ //}
+ //
+
if (target_type >= LOOKAT_NUM_TARGETS)
{
LL_WARNS() << "Bad target_type " << (int)target_type << " - ignoring." << LL_ENDL;
@@ -475,6 +493,31 @@ bool LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *objec
return false;
}
+ // Prefer custom FS lookat privacy code
+ //static LLCachedControl limit_lookat_hints(gSavedSettings, "LimitLookAtTarget", true);
+ //// Don't affect the look at if object is gAgentAvatarp (cursor head follow)
+ //if (limit_lookat_hints && object != gAgentAvatarp)
+ //{
+ // // If it is a object
+ // if (object)
+ // {
+ // position += object->getRenderPosition();
+ // object = NULL;
+ // }
+
+ // LLVector3 agentHeadPosition = gAgentAvatarp->mHeadp->getWorldPosition();
+ // float dist = (float)dist_vec(agentHeadPosition, position);
+
+ // static LLCachedControl limit_lookat_hints_distance(gSavedSettings, "LimitLookAtTargetDistance", 2.0f);
+ // if (dist > limit_lookat_hints_distance)
+ // {
+ // LLVector3 headOffset = position - agentHeadPosition;
+ // headOffset *= limit_lookat_hints_distance / dist;
+ // position.setVec(agentHeadPosition + headOffset);
+ // }
+ //}
+ //
+
F32 current_time = mTimer.getElapsedTimeF32();
// FIRE-23524 Option to limit look at target to a sphere around the avatar's head.
diff --git a/indra/newview/llhudeffectpointat.cpp b/indra/newview/llhudeffectpointat.cpp
index 7551b2a9b1..47ed41b881 100644
--- a/indra/newview/llhudeffectpointat.cpp
+++ b/indra/newview/llhudeffectpointat.cpp
@@ -34,6 +34,7 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "lldrawable.h"
+#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
#include "message.h"
@@ -250,6 +251,21 @@ bool LLHUDEffectPointAt::setPointAt(EPointAtType target_type, LLViewerObject *ob
return false;
}
+ // Prefer custom FS lookat privacy code
+ //static LLCachedControl enable_selection_hints(gSavedSettings, "EnableSelectionHints", true);
+ //if (!enable_selection_hints)
+ //{
+ // // Clear the effect so it doesn't linger around if it gets disabled
+ // if (mTargetType != POINTAT_TARGET_NONE)
+ // {
+ // clearPointAtTarget();
+ // setDuration(1.f);
+ // setNeedsSendToSim(true);
+ // }
+ // return false;
+ //}
+ //
+
if (target_type >= POINTAT_NUM_TARGETS)
{
LL_WARNS() << "Bad target_type " << (int)target_type << " - ignoring." << LL_ENDL;
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 1fbdc47ff1..72d4e689f5 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"
@@ -263,6 +264,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 b823517313..806a6a88fa 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -101,7 +101,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/llimprocessing.cpp b/indra/newview/llimprocessing.cpp
index e60769362e..f9c2919a79 100644
--- a/indra/newview/llimprocessing.cpp
+++ b/indra/newview/llimprocessing.cpp
@@ -2273,10 +2273,10 @@ void LLIMProcessing::requestOfflineMessages()
if (!requested
&& gMessageSystem
&& !gDisconnected
- && LLMuteList::getInstance()->isLoaded()
&& isAgentAvatarValid()
&& gAgent.getRegion()
- && gAgent.getRegion()->capabilitiesReceived())
+ && gAgent.getRegion()->capabilitiesReceived()
+ && (LLMuteList::getInstance()->isLoaded() || LLMuteList::getInstance()->getLoadFailed()))
{
std::string cap_url = gAgent.getRegionCapability("ReadOfflineMsgs");
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 3db291ed40..47385e5e51 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -1922,6 +1922,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 499aaa1a85..72ffc5bb6a 100644
--- a/indra/newview/llinspecttexture.cpp
+++ b/indra/newview/llinspecttexture.cpp
@@ -122,7 +122,6 @@ public:
protected:
LLPointer m_Image;
- S32 mImageBoostLevel = LLGLTexture::BOOST_NONE;
std::string mLoadingText;
};
@@ -135,12 +134,8 @@ LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p)
LLTexturePreviewView::~LLTexturePreviewView()
{
- if (m_Image)
- {
- m_Image->setBoostLevel(mImageBoostLevel);
m_Image = nullptr;
}
-}
void LLTexturePreviewView::draw()
{
@@ -160,18 +155,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 d456d40321..f6033d6c04 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -873,6 +873,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)
{
@@ -890,7 +892,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"));
@@ -903,6 +906,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"));
@@ -915,6 +931,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
@@ -924,7 +941,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"));
// Locked folder
@@ -967,6 +984,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())
@@ -992,8 +1010,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
// Enable paste for inbox; doesn't actually makes much sense,
// but since we are not prevented from pasting via shortcut,
// we enable it in the context menu, too.
- //if (!isCOFFolder() && !isInboxFolder()
- if (!isCOFFolder()
+ //if (!is_cof && !is_inbox)
+ if (!is_cof
// Client LSL Bridge (also for #AO)
&& !isLockedFolder())
//
@@ -1434,6 +1452,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;
+}
+
// [SL:KB] - Patch: Inventory-Misc | Checked: 2011-05-28 (Catznip-2.6.0a) | Added: Catznip-2.6.0a
bool LLInvFVBridge::isLibraryInventory() const
{
@@ -2519,7 +2544,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())
@@ -2692,6 +2731,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)
@@ -4828,6 +4877,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"));
// Undo weird menu design
disabled_items.push_back(std::string("New Script"));
disabled_items.push_back(std::string("New Note"));
@@ -4862,6 +4912,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"));
// Undo weird menu design
disabled_items.push_back(std::string("New Script"));
disabled_items.push_back(std::string("New Note"));
@@ -4933,6 +4984,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();
@@ -4958,6 +5010,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'
@@ -4967,6 +5020,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
@@ -4983,6 +5037,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")); // Undo weird menu design
items.push_back(std::string("New Script"));
@@ -5014,6 +5069,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);
}
@@ -5332,6 +5389,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();
@@ -7826,16 +7895,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));
-// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2013-02-04 (Catznip-3.4)
- // "Wear" from inventory replaces, so library items should too
LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, true));
-// [/SL;KB]
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
@@ -8586,6 +8652,15 @@ void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Properties"));
addDeleteContextMenuOptions(items, disabled_items);
+
+ if (isFavorite())
+ {
+ items.push_back(std::string("Remove from Favorites"));
+ }
+ else if (isAgentInventory())
+ {
+ items.push_back(std::string("Add to Favorites"));
+ }
}
addLinkReplaceMenuOption(items, disabled_items);
hide_context_entries(menu, items, disabled_items);
@@ -8815,6 +8890,15 @@ void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
{
items.push_back(std::string("Find Original"));
addDeleteContextMenuOptions(items, disabled_items);
+
+ if (isFavorite())
+ {
+ items.push_back(std::string("Remove from Favorites"));
+ }
+ else if (isAgentInventory())
+ {
+ items.push_back(std::string("Add to Favorites"));
+ }
}
hide_context_entries(menu, items, disabled_items);
}
@@ -9074,7 +9158,8 @@ void LLObjectBridgeAction::attachOrDetach()
else
{
// Double-click add/replace option
- //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.
LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, !gSavedSettings.getBOOL("FSDoubleClickAddInventoryObjects")); // Don't replace if adding.
}
}
@@ -9268,6 +9353,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);
}
@@ -9302,6 +9388,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()
{
}
@@ -9313,8 +9444,8 @@ void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_dequ
if (get_selection_item_uuids(selected_items, ids))
{
// Fix broken add wearable check
- //if (!LLAppearanceMgr::instance().canAddWearables(ids) && canWearSelected(ids))
- if (!canWearSelected(ids) || !LLAppearanceMgr::instance().canAddWearables(ids))
+ //if (!LLAppearanceMgr::instance().canAddWearables(ids, false) && canWearSelected(ids))
+ if (!canWearSelected(ids) || !LLAppearanceMgr::instance().canAddWearables(ids, false))
{
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 d6ab549cf1..bc688bce9c 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() {}
@@ -182,6 +183,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
@@ -277,6 +279,7 @@ public:
LLViewerInventoryItem* getItem() const;
virtual const LLUUID& getThumbnailUUID() const;
+ virtual bool isFavorite() const;
protected:
bool confirmRemoveItem(const LLSD& notification, const LLSD& response);
@@ -319,6 +322,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;}
@@ -368,6 +372,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
@@ -781,6 +786,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;
+};
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Worn Inventory Panel related classes
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -845,7 +890,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 ee57deecb7..1d1b148a47 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -72,6 +72,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p)
mFilterUUID(p.uuid),
mFilterLinks(p.links),
mFilterThumbnails(p.thumbnails),
+ mFilterFavorites(p.favorites),
mCoalescedObjectsOnly(p.coalesced_objects_only), // FIRE-31369: Add inventory filter for coalesced objects
mSearchVisibility(p.search_visibility)
{
@@ -178,6 +179,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;
@@ -240,6 +242,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 |
@@ -292,6 +307,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)
@@ -671,6 +696,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)
@@ -905,6 +948,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;
@@ -1017,6 +1086,11 @@ void LLInventoryFilter::toggleSearchVisibilityLibrary()
}
}
+void LLInventoryFilter::setFilterNoTrashFolder()
+{
+ mFilterOps.mFilterTypes |= FILTERTYPE_NO_TRASH_ITEMS;
+}
+
void LLInventoryFilter::setFilterNoMarketplaceFolder()
{
mFilterOps.mFilterTypes |= FILTERTYPE_NO_MARKETPLACE_ITEMS;
@@ -1856,6 +1930,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 804ca551ad..276043cace 100644
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -60,7 +60,8 @@ public:
FILTERTYPE_MARKETPLACE_LISTING_FOLDER = 0x1 << 9, // pass iff folder is a listing folder
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_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
@@ -150,6 +158,7 @@ public:
Optional permissions;
Optional creator_type;
Optional thumbnails;
+ Optional favorites;
Optional coalesced_objects_only; // FIRE-31369: Add inventory filter for coalesced objects
Params()
@@ -158,6 +167,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),
@@ -180,6 +190,7 @@ public:
mFilterWearableTypes,
mFilterSettingsTypes, // for _SETTINGS
mFilterThumbnails,
+ mFilterFavorites,
mFilterLinks,
mFilterCategoryTypes; // For _CATEGORY
LLUUID mFilterUUID; // for UUID
@@ -225,6 +236,7 @@ public:
U64 getFilterSettingsTypes() const;
U64 getSearchVisibilityTypes() const;
U64 getFilterThumbnails() const;
+ U64 getFilterFavorites() const;
bool isFilterObjectTypesWith(LLInventoryType::EType t) const;
void setFilterObjectTypes(U64 types);
@@ -239,8 +251,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; }
@@ -361,6 +375,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 80e2053e8c..34dd1047ef 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"
@@ -2326,21 +2329,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);
}
}
@@ -2348,17 +2340,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);
}
}
}
@@ -2661,6 +2653,143 @@ void ungroup_folder_items(const LLUUID& folder_id)
}); // FIRE-32736: Add confirmation before ungrouping folder
}
+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)
@@ -2999,6 +3128,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)
{
@@ -3871,7 +4014,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)
{
@@ -3915,6 +4057,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)
@@ -4037,6 +4193,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);
@@ -4145,15 +4349,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 2c9df1baf1..d974b3ca0e 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -129,6 +129,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);
@@ -212,7 +217,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);
};
@@ -386,6 +393,18 @@ protected:
LLUUID mGroupID;
};
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFavoritesCollector
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLFavoritesCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLFavoritesCollector() {}
+ virtual ~LLFavoritesCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLBuddyCollector
//
@@ -660,6 +679,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 79194aaec9..5c990f5727 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);
}
}
}
@@ -2341,7 +2354,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;
}
@@ -2857,6 +2870,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 244ae8d6fa..4171ac10d4 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);
@@ -484,22 +498,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)
@@ -780,6 +779,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"));
}
@@ -799,6 +799,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)
{
@@ -844,6 +856,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"));
@@ -980,6 +1003,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"));
}
@@ -1030,6 +1054,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 452a195a3f..d3cb2dfe82 100644
--- a/indra/newview/llinventorylistitem.cpp
+++ b/indra/newview/llinventorylistitem.cpp
@@ -47,7 +47,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"),
@@ -80,19 +92,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)
@@ -100,7 +123,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();
@@ -112,9 +135,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);
}
@@ -169,7 +198,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);
@@ -295,6 +324,7 @@ LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem
mHovered(false),
mSelected(false),
mSeparatorVisible(false),
+ mIsFavorite(false),
mHoverImage(params.hover_image),
mSelectedImage(params.selected_image),
mSeparatorImage(params.separator_image)
@@ -413,6 +443,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,
@@ -518,6 +558,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 cf9032a367..0ac7ef01e2 100644
--- a/indra/newview/llinventorylistitem.h
+++ b/indra/newview/llinventorylistitem.h
@@ -169,6 +169,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; }
@@ -226,8 +227,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 e03a2bc77f..cb6b4ab1ee 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -94,7 +94,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;
@@ -1377,6 +1377,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
{
for (auto& cat : *cat_array)
{
+ if (add.exceedsLimit())
+ {
+ break;
+ }
if(add(cat,NULL))
{
cats.push_back(cat);
@@ -1395,6 +1399,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
{
for (auto& item : *item_array)
{
+ if (add.exceedsLimit())
+ {
+ break;
+ }
if(add(NULL, item))
{
items.push_back(item);
@@ -3047,8 +3055,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());
}
}
@@ -3655,7 +3664,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())
{
@@ -3664,80 +3673,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();
@@ -3760,54 +3781,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 4a3634ca43..d60d31d2ad 100644
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -786,6 +786,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();
@@ -803,6 +810,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);
@@ -816,6 +824,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;
@@ -841,11 +850,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)));
}
}
@@ -858,25 +867,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 9faaeeecb6..c613691995 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -61,11 +61,13 @@
// [/RLVa:KB]
#include "fsfloaterpartialinventory.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");
@@ -709,6 +711,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
@@ -737,6 +752,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);
+ }
+ }
+
}
//////////////////////////////
@@ -750,9 +775,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)
{
@@ -787,6 +814,18 @@ 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
+ }
+ }
}
}
}
@@ -799,6 +838,7 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve
// Remove the item's UI.
LLFolderViewFolder* parent = view_item->getParentFolder();
removeItemID(viewmodel_item->getUUID());
+ bool was_favorite = view_item->isFavorite();
view_item->destroyView();
if(parent)
{
@@ -812,6 +852,10 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve
updateFolderLabel(viewmodel_folder->getUUID());
}
}
+ if (was_favorite)
+ {
+ parent->updateHasFavorites(false); // favorite was removed
+ }
}
}
}
@@ -822,7 +866,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;
@@ -929,7 +973,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();
@@ -969,6 +1029,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())
@@ -1011,20 +1076,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;
}
@@ -1055,6 +1109,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)
{
@@ -1958,26 +2028,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)
@@ -2550,6 +2602,297 @@ 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;
+ bool hasPredecessorsInRootContent(const LLInventoryObject* model_item) const;
+
+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();
+}
+
+bool LLInventoryFavoritesItemsPanel::hasPredecessorsInRootContent(const LLInventoryObject* obj) const
+{
+ LLUUID parent_id = obj->getParentUUID();
+ while (parent_id.notNull())
+ {
+ if (mRootContentIDs.contains(parent_id))
+ {
+ return true;
+ }
+ LLViewerInventoryCategory* cat = mInventory->getCategory(parent_id);
+ if (cat)
+ {
+ parent_id = cat->getParentUUID();
+ }
+ }
+ return false;
+}
+
+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());
+ }
+ bool was_favorite = view_item->isFavorite();
+ view_item->destroyView();
+ if (parent)
+ {
+ parent->getViewModelItem()->dirtyDescendantsFilter();
+ LLFolderViewModelItemInventory* viewmodel_folder = static_cast(parent->getViewModelItem());
+ if (viewmodel_folder)
+ {
+ updateFolderLabel(viewmodel_folder->getUUID());
+ }
+ if (was_favorite)
+ {
+ 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
+ && !hasPredecessorsInRootContent(model_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)
+ && !hasPredecessorsInRootContent(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 9496871856..758e8b4ded 100644
--- a/indra/newview/llinventorypanel.h
+++ b/indra/newview/llinventorypanel.h
@@ -269,7 +269,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);
@@ -353,6 +354,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;
@@ -390,7 +393,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,
@@ -413,6 +416,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 5a480c3c4b..6706c2aa48 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"
@@ -445,13 +447,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())
@@ -525,8 +543,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;
@@ -563,6 +581,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])
{
@@ -1306,7 +1337,7 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list
- fptr = LLFile::fopen(LLLogChat::makeLogFileName(old_name), "r");
+ fptr = LLFile::fopen(LLLogChat::makeLogFileName(old_name), "rb");
if (fptr)
{
fclose(fptr);
LLFile::copy(LLLogChat::makeLogFileName(old_name), LLLogChat::makeLogFileName(file_name));
}
- fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");
+ fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "rb");
}
if (!fptr)
{
- fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
+ fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "rb");/*Flawfinder: ignore*/
if (!fptr)
{
mNewLoad = false;
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 8a20d15d6f..d5c4959bf6 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -1425,7 +1425,7 @@ bool LLMaterialEditor::saveIfNeeded()
}
std::string res_desc = buildMaterialDescription();
- createInventoryItem(buffer, mMaterialName, res_desc, local_permissions);
+ createInventoryItem(buffer, mMaterialName, res_desc, local_permissions, mUploadFolder);
// We do not update floater with uploaded asset yet, so just close it.
closeFloater();
@@ -1595,12 +1595,12 @@ private:
std::string mNewName;
};
-void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions)
+void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions, const LLUUID& upload_folder)
{
// gen a new uuid for this asset
LLTransactionID tid;
tid.generate(); // timestamp-based randomization + uniquification
- LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL);
+ LLUUID parent = upload_folder.isNull() ? gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL) : upload_folder;
const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ?
LLPointer cb = new LLObjectsMaterialItemCallback(permissions, buffer, name);
@@ -1914,7 +1914,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())
{
@@ -1937,12 +1941,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;
@@ -2442,17 +2447,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;
}
@@ -2888,10 +2893,10 @@ void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const ti
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
//
-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())
{
@@ -2901,7 +2906,7 @@ void LLMaterialEditor::importMaterial()
{
if (filenames.size() > 0)
{
- LLMaterialEditor::loadMaterialFromFile(filenames[0], -1);
+ LLMaterialEditor::loadMaterialFromFile(filenames[0], -1, dest_folder);
}
}
catch (std::bad_alloc&)
@@ -3594,6 +3599,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 8e05d65c67..9e765bab82 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -832,8 +832,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;
@@ -891,6 +895,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
{
@@ -2469,7 +2487,13 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
LLPointer volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
if (volume->unpackVolumeFaces(data, data_size))
{
- if (volume->getNumFaces() > 0)
+ // Use LLVolume::getNumVolumeFaces() here and not LLVolume::getNumFaces(),
+ // because setMeshAssetLoaded() has not yet been called for this volume
+ // (it is set later in LLMeshRepository::notifyMeshLoaded()), and
+ // getNumFaces() would return the number of faces in the LLProfile
+ // instead. HB
+ S32 num_faces = volume->getNumVolumeFaces();
+ if (num_faces > 0)
{
// if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD
LLPointer skin_info = nullptr;
@@ -2483,7 +2507,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
}
if (skin_info.notNull() && isAgentAvatarValid())
{
- for (S32 i = 0; i < volume->getNumFaces(); ++i)
+ for (S32 i = 0; i < num_faces; ++i)
{
// NOTE: no need to lock gAgentAvatarp as the state being checked is not changed after initialization
LLVolumeFace& face = volume->getVolumeFace(i);
@@ -2652,9 +2676,10 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_
return MESH_OK;
}
-LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, const LLMeshUploadThread::lod_sources_map_t& sources_list,
+ 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"),
@@ -2662,10 +2687,12 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
mDiscarded(false),
mDoUpload(do_upload),
mWholeModelUploadURL(upload_url),
+ mDestinationFolderId(destination_folder_id),
mFeeObserverHandle(fee_observer),
mUploadObserverHandle(upload_observer)
{
mInstanceList = data;
+ mLodSources = sources_list;
mUploadTextures = upload_textures;
mUploadSkin = upload_skin;
mUploadJoints = upload_joints;
@@ -2765,6 +2792,8 @@ void dump_llsd_to_file(const LLSD& content, std::string filename)
{
if (gSavedSettings.getBOOL("MeshUploadLogXML"))
{
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+ filename);
llofstream of(filename.c_str());
LLSDSerialize::toPrettyXML(content,of);
}
@@ -2778,13 +2807,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)";
@@ -2795,6 +2832,12 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
res["mesh_list"] = LLSD::emptyArray();
res["texture_list"] = LLSD::emptyArray();
res["instance_list"] = LLSD::emptyArray();
+ LLSD& lod_sources = res["source_format"];
+ lod_sources["high"] = 0;
+ for (auto &source : mLodSources)
+ {
+ lod_sources[source.first] = source.second;
+ }
S32 mesh_num = 0;
S32 texture_num = 0;
@@ -2931,7 +2974,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());
}
@@ -2945,6 +2988,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++;
}
@@ -3210,7 +3255,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));
@@ -3263,7 +3309,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,
@@ -3329,7 +3376,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)
{
@@ -3364,7 +3411,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)
{
@@ -3389,7 +3436,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)
{
@@ -3422,7 +3469,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)
{
@@ -5192,14 +5239,15 @@ LLUUID LLMeshRepoThread::getCreatorFromHeader(const LLUUID& mesh_id)
}
//
-void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures,
+void LLMeshRepository::uploadModel(std::vector& data, const std::map &lod_sources,
+ 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,
+ LLMeshUploadThread* thread = new LLMeshUploadThread(data, lod_sources, 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 1be2db610c..c55e800313 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -719,6 +719,8 @@ public:
};
typedef std::map, instance_list, LLUploadModelInstanceLess> instance_map;
instance_map mInstance;
+ typedef std::map lod_sources_map_t;
+ lod_sources_map_t mLodSources;
LLMutex* mMutex;
S32 mPendingUploads;
@@ -733,10 +735,14 @@ public:
LLHost mHost;
std::string mWholeModelFeeCapability;
std::string mWholeModelUploadURL;
+ LLUUID mDestinationFolderId;
- LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures,
+ LLMeshUploadThread(instance_list& data, const lod_sources_map_t& sources_list,
+ 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();
@@ -752,7 +758,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,
@@ -773,6 +779,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;
@@ -896,9 +903,12 @@ public:
bool meshUploadEnabled();
bool meshRezEnabled();
- void uploadModel(std::vector& data, LLVector3& scale, bool upload_textures,
+ void uploadModel(std::vector& data, const std::map &lod_sources,
+ 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 652e8a4b9a..248843c8d1 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -3268,7 +3268,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;
@@ -4473,7 +4473,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/llmutelist.cpp b/indra/newview/llmutelist.cpp
index f7fd8159ab..918519660c 100644
--- a/indra/newview/llmutelist.cpp
+++ b/indra/newview/llmutelist.cpp
@@ -157,7 +157,8 @@ std::string LLMute::getDisplayType() const
// LLMuteList()
//-----------------------------------------------------------------------------
LLMuteList::LLMuteList() :
- mIsLoaded(false)
+ mLoadState(ML_INITIAL),
+ mRequestStartTime(0.f)
{
gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
@@ -212,6 +213,23 @@ bool LLMuteList::isLinden(const std::string& name)
return last_name == "linden";
}
+bool LLMuteList::getLoadFailed() const
+{
+ if (mLoadState == ML_FAILED)
+ {
+ return true;
+ }
+ if (mLoadState == ML_REQUESTED)
+ {
+ constexpr F64 WAIT_SECONDS = 30;
+ if (mRequestStartTime + WAIT_SECONDS < LLTimer::getTotalSeconds())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
static LLVOAvatar* find_avatar(const LLUUID& id)
{
LLViewerObject *obj = gObjectList.findObject(id);
@@ -389,11 +407,14 @@ void LLMuteList::updateAdd(const LLMute& mute, bool show_message /* = true */)
msg->addU32("MuteFlags", mute.mFlags);
gAgent.sendReliableMessage();
- if (!mIsLoaded)
+ if (!isLoaded())
{
LL_WARNS() << "Added elements to non-initialized block list" << LL_ENDL;
}
- mIsLoaded = true; // why is this here? -MG
+ // Based of logs and testing, if file doesn't exist server side,
+ // viewer will not receive any callback and won't know to set
+ // ML_LOADED. As a workaround, set it regardless of current state.
+ mLoadState = ML_LOADED;
// FIRE-15746: Show block report in nearby chat
if (show_message && gSavedSettings.getBOOL("FSReportBlockToNearbyChat"))
@@ -609,6 +630,7 @@ bool LLMuteList::loadFromFile(const std::string& filename)
if(!filename.size())
{
LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL;
+ mLoadState = ML_FAILED;
return false;
}
@@ -616,6 +638,7 @@ bool LLMuteList::loadFromFile(const std::string& filename)
if (!fp)
{
LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL;
+ mLoadState = ML_FAILED;
return false;
}
@@ -791,13 +814,17 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id)
if (gDisconnected)
{
LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL;
+ mLoadState = ML_FAILED;
return;
}
if (!gAgent.getRegion())
{
LL_WARNS() << "No region for agent yet, skipping mute list request!" << LL_ENDL;
+ mLoadState = ML_FAILED;
return;
}
+ mLoadState = ML_REQUESTED;
+ mRequestStartTime = LLTimer::getElapsedSeconds();
// Double amount of retries due to this request happening during busy stage
// Ideally this should be turned into a capability
gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL);
@@ -810,7 +837,7 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id)
void LLMuteList::cache(const LLUUID& agent_id)
{
// Write to disk even if empty.
- if(mIsLoaded)
+ if(isLoaded())
{
std::string agent_id_string;
std::string filename;
@@ -838,6 +865,13 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)
msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, unclean_filename);
std::string filename = LLDir::getScrubbedFileName(unclean_filename);
+ LLMuteList* mute_list = getInstance();
+ mute_list->mLoadState = ML_REQUESTED;
+ mute_list->mRequestStartTime = LLTimer::getElapsedSeconds();
+
+ // Todo: Based of logs and testing, there is no callback
+ // from server if file doesn't exist server side.
+ // Once server side gets fixed make sure it gets handled right.
std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));
gXferManager->requestFile(*local_filename_and_path,
filename,
@@ -870,12 +904,16 @@ void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_
LLMuteList::getInstance()->loadFromFile(*local_filename_and_path);
LLFile::remove(*local_filename_and_path);
}
+ else
+ {
+ LLMuteList::getInstance()->mLoadState = ML_FAILED;
+ }
delete local_filename_and_path;
}
void LLMuteList::onAccountNameChanged(const LLUUID& id, const std::string& username)
{
- if (mIsLoaded)
+ if (isLoaded())
{
LLMute mute(id, username, LLMute::AGENT);
mute_set_t::iterator mute_it = mMutes.find(mute);
@@ -927,7 +965,7 @@ void LLMuteList::removeObserver(LLMuteListObserver* observer)
void LLMuteList::setLoaded()
{
- mIsLoaded = true;
+ mLoadState = ML_LOADED;
notifyObservers();
}
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
index f71c581da4..98efe7a0c3 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -74,6 +74,14 @@ class LLMuteList : public LLSingleton
LLSINGLETON(LLMuteList);
~LLMuteList();
/*virtual*/ void cleanupSingleton() override;
+
+ enum EMuteListState
+ {
+ ML_INITIAL,
+ ML_REQUESTED,
+ ML_LOADED,
+ ML_FAILED,
+ };
public:
// reasons for auto-unmuting a resident
enum EAutoReason
@@ -107,7 +115,8 @@ public:
static bool isLinden(const std::string& name);
- bool isLoaded() const { return mIsLoaded; }
+ bool isLoaded() const { return mLoadState == ML_LOADED; }
+ bool getLoadFailed() const;
std::vector getMutes() const;
@@ -170,7 +179,8 @@ private:
typedef std::set observer_set_t;
observer_set_t mObservers;
- bool mIsLoaded;
+ EMuteListState mLoadState;
+ F64 mRequestStartTime;
friend class LLDispatchEmptyMuteList;
};
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index f1932783d3..fe18dc1861 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,24 +1277,11 @@ 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("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);
@@ -1342,6 +1398,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);
@@ -1357,3 +1422,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..9a0bdb986d 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,8 @@ private:
typedef item_num_map_t::value_type item_numb_map_value_t;
item_num_map_t mItemIndexMap;
std::map mIndexToItemMap;
+
+ boost::signals2::connection mSavedSettingInvFavColor;
};
class LLOutfitGalleryContextMenu : public LLOutfitContextMenu
{
@@ -210,8 +218,6 @@ public:
protected:
/*virtual*/ void onUpdateItemsVisibility();
private:
- /*virtual*/ void onChangeSortOrder();
-
bool hasDefaultImage();
};
@@ -240,6 +246,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 +254,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 +272,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 9ec14e79f4..9b35bf425d 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -34,10 +34,13 @@
#include "llaccordionctrl.h"
#include "llaccordionctrltab.h"
#include "llagentwearables.h"
+#include "llaisapi.h"
#include "llappearancemgr.h"
+#include "llappviewer.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llinspecttexture.h"
+#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "llmenubutton.h"
@@ -45,6 +48,7 @@
#include "lloutfitobserver.h"
#include "lltoggleablemenu.h"
#include "lltransutil.h"
+#include "llviewercontrol.h"
#include "llviewermenu.h"
#include "llvoavatar.h"
#include "llvoavatarself.h"
@@ -61,14 +65,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
@@ -88,6 +102,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))
{
@@ -111,11 +128,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()
@@ -123,9 +149,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)
{
@@ -174,6 +216,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);
@@ -207,8 +250,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));
@@ -218,12 +262,27 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3));
- // Fetch the new outfit contents.
- cat->fetch();
-
- // Refresh the list of outfit items after fetch().
- // Further list updates will be triggered by the category observer.
- list->updateList(cat_id);
+ if (AISAPI::isAvailable() && LLInventoryModelBackgroundFetch::instance().folderFetchActive())
+ {
+ // for reliability just fetch it whole, linked items included
+ LLInventoryModelBackgroundFetch::instance().fetchFolderAndLinks(cat_id, [cat_id, list]
+ {
+ if (list)
+ {
+ list->updateList(cat_id);
+ list->setForceRefresh(true);
+ }
+ });
+ }
+ else
+ {
+ // Fetch the new outfit contents.
+ cat->fetch();
+ // Refresh the list of outfit items after fetch().
+ // Further list updates will be triggered by the category observer.
+ list->updateList(cat_id);
+ list->setForceRefresh(true);
+ }
// If filter is currently applied we store the initial tab state.
if (!getFilterSubString().empty())
@@ -326,13 +385,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);
}
}
@@ -394,6 +451,11 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid)
}
}
+void LLOutfitListBase::onAction(const LLSD& userdata)
+{
+ performAction(userdata.asString());
+}
+
// virtual
bool LLOutfitListBase::isActionEnabled(const LLSD& userdata)
{
@@ -506,11 +568,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());
}
}
}
@@ -819,6 +882,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);
@@ -836,11 +968,11 @@ bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y)
LLOutfitListBase::LLOutfitListBase()
: LLPanelAppearanceTab()
, mIsInitialized(false)
+ , mGearMenu(nullptr)
, mAvatarComplexityLabel(NULL) // Show avatar complexity in appearance floater
{
mCategoriesObserver = new LLInventoryCategoriesObserver();
mOutfitMenu = new LLOutfitContextMenu(this);
- //mGearMenu = createGearMenu();
}
LLOutfitListBase::~LLOutfitListBase()
@@ -936,6 +1068,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();
@@ -1008,8 +1144,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();
@@ -1164,15 +1310,8 @@ void LLOutfitListBase::ChangeOutfitSelection(LLWearableItemsList* list, const LL
bool LLOutfitListBase::postBuild()
{
- mGearMenu = createGearMenu();
-
// Show avatar complexity in appearance floater
mAvatarComplexityLabel = getChild("avatar_complexity_label");
-
- 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;
}
@@ -1186,6 +1325,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.
@@ -1223,6 +1376,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));
@@ -1273,6 +1427,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;
}
@@ -1297,6 +1461,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())
@@ -1327,14 +1499,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));
@@ -1468,6 +1639,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;
}
@@ -1479,6 +1660,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()
{
@@ -1494,13 +1681,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)
{
// Make thumbnail tooltip work properly
@@ -1539,4 +1792,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 9ec6e4c0f5..38a39f0332 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;
class LLTextBox;
@@ -62,6 +63,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:
@@ -94,6 +106,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;
@@ -111,6 +124,12 @@ public:
virtual bool getHasExpandableFolders() = 0;
+ virtual void onChangeSortOrder(const LLSD& userdata) = 0;
+
+ virtual void updateMenuItemsVisibility();
+ virtual LLToggleableMenu* getGearMenu();
+ virtual bool getTrashMenuVisible() { return true; };
+
// Show avatar complexity in appearance floater
void updateAvatarComplexity(U32 complexity);
@@ -146,6 +165,7 @@ protected:
selection_change_signal_t mSelectionChangeSignal;
LLListContextMenu* mOutfitMenu;
LLOutfitListGearMenuBase* mGearMenu;
+ boost::signals2::connection mGearMenuConnection;
// Show avatar complexity in appearance floater
LLTextBox* mAvatarComplexityLabel;
};
@@ -164,7 +184,6 @@ protected:
/* virtual */ LLContextMenu* createMenu();
bool onEnable(LLSD::String param);
-
bool onVisible(LLSD::String param);
static void editOutfit();
@@ -172,6 +191,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:
@@ -191,6 +211,7 @@ public:
protected:
virtual void onUpdateItemsVisibility();
virtual void onThumbnail();
+ virtual void onFavorite();
virtual void onChangeSortOrder();
const LLUUID& getSelectedOutfitID();
@@ -211,6 +232,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:
@@ -230,8 +268,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),
@@ -239,7 +285,12 @@ public:
{}
friend class LLUICtrlFactory;
+ void updateTitleColor();
+ void drawFavoriteIcon();
+
LLUUID mFolderID;
+ bool mIsFavorite = false;
+ bool mIsSelected = false;
};
/**
* @class LLOutfitsList
@@ -258,6 +309,7 @@ public:
virtual ~LLOutfitsList();
/*virtual*/ bool postBuild();
+ void initComparator();
/*virtual*/ void onOpen(const LLSD& info);
@@ -296,6 +348,10 @@ public:
/*virtual*/ bool getHasExpandableFolders() { return true; }
+ /*virtual*/ void onChangeSortOrder(const LLSD& userdata);
+ virtual LLToggleableMenu* getSortMenu();
+ void updateMenuItemsVisibility();
+
protected:
LLOutfitListGearMenuBase* createGearMenu();
@@ -367,6 +423,8 @@ private:
static void onOutfitRename(const LLSD& notification, const LLSD& response);
+ void handleInvFavColorChange();
+
// FIRE-22484: Double-click wear in outfits list
void onDoubleClick(LLWearableItemsList* list);
@@ -387,13 +445,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 1ed8e233ce..d00a7bb7b0 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -588,10 +588,6 @@ LLPanelFace::~LLPanelFace()
void LLPanelFace::onVisibilityChange(bool new_visibility)
{
- if (new_visibility)
- {
- gAgent.showLatestFeatureNotification("gltf");
- }
LLPanel::onVisibilityChange(new_visibility);
}
@@ -2305,7 +2301,7 @@ void LLPanelFace::refreshMedia()
// check if all faces have media(or, all dont have media)
- LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue(&func, bool_has_media);
+ bool identical_has_media_info = selected_objects->getSelectedTEValue(&func, bool_has_media);
const LLMediaEntry default_media_data;
@@ -2327,7 +2323,8 @@ void LLPanelFace::refreshMedia()
} func_media_data(default_media_data);
LLMediaEntry media_data_get;
- LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
+ bool multiple_media = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
+ bool multiple_valid_media = false;
std::string multi_media_info_str = LLTrans::getString("Multiple Media");
std::string media_title = "";
@@ -2336,12 +2333,12 @@ void LLPanelFace::refreshMedia()
mAddMedia->setEnabled(editable);
// IF all the faces have media (or all dont have media)
- if (LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo)
+ if (identical_has_media_info)
{
// TODO: get media title and set it.
mTitleMediaText->clear();
// if identical is set, all faces are same (whether all empty or has the same media)
- if (!(LLFloaterMediaSettings::getInstance()->mMultipleMedia))
+ if (!multiple_media)
{
// Media data is valid
if (media_data_get != default_media_data)
@@ -2362,9 +2359,9 @@ void LLPanelFace::refreshMedia()
else // not all face has media but at least one does.
{
// seleted faces have not identical value
- LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
+ multiple_valid_media = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
- if (LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
+ if (multiple_valid_media)
{
media_title = multi_media_info_str;
}
@@ -2401,7 +2398,7 @@ void LLPanelFace::refreshMedia()
// load values for media settings
updateMediaSettings();
- LLFloaterMediaSettings::initValues(mMediaSettings, editable);
+ LLFloaterMediaSettings::initValues(mMediaSettings, editable, identical_has_media_info, multiple_media, multiple_valid_media);
}
void LLPanelFace::unloadMedia()
@@ -3486,6 +3483,7 @@ void LLPanelFace::onSelectNormalTexture(const LLSD& data)
// TODO: test if there is media on the item and only allow editing if present
void LLPanelFace::onClickBtnEditMedia()
{
+ LLFloaterMediaSettings::getInstance(); // make sure floater we are about to open exists before refreshMedia
refreshMedia();
LLFloaterReg::showInstance("media_settings");
}
@@ -3504,6 +3502,7 @@ void LLPanelFace::onClickBtnAddMedia()
// check if multiple faces are selected
if (LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected())
{
+ LLFloaterMediaSettings::getInstance(); // make sure floater we are about to open exists before refreshMedia
refreshMedia();
LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm);
}
diff --git a/indra/newview/llpanelgroupcreate.cpp b/indra/newview/llpanelgroupcreate.cpp
index eb3b9c7661..bd580eaa01 100644
--- a/indra/newview/llpanelgroupcreate.cpp
+++ b/indra/newview/llpanelgroupcreate.cpp
@@ -90,6 +90,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 2ab6c4fd4c..c6a16ec692 100644
--- a/indra/newview/llpanelgroupgeneral.cpp
+++ b/indra/newview/llpanelgroupgeneral.cpp
@@ -262,6 +262,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 13107d994b..7c6db6ce80 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -77,6 +77,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");
@@ -337,6 +338,17 @@ bool LLPanelMainInventory::postBuild()
}
//
}
+
+ 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));
+ }
+
// Only if we actually have it!
//mSearchTypeCombo = getChild("search_type");
mSearchTypeCombo = findChild("search_type");
@@ -740,7 +752,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 92d3d1f3f2..cc7b28392a 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -2956,19 +2956,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();
+ }
}
// Extended copy & paste buttons
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index df3f7bf542..1de859aa80 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -136,6 +136,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();
@@ -1530,7 +1531,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 d6b191e3de..e621f16a84 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -409,6 +409,8 @@ LLPanelOutfitEdit::LLPanelOutfitEdit()
mPlusBtn(NULL),
mWearablesGearMenuBtn(NULL),
mGearMenuBtn(NULL),
+ mStatus(NULL),
+ mCurrentOutfitName(NULL),
// Show avatar complexity in appearance floater
mAvatarComplexityLabel(NULL),
mAvatarComplexityAddingLabel(NULL)
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 3d286984b6..b6ae97e834 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));
@@ -78,6 +84,9 @@ LLPanelOutfitsInventory::~LLPanelOutfitsInventory()
{
gSavedSettings.setS32("LastAppearanceTab", mAppearanceTabs->getCurrentPanelIndex());
}
+ mGearMenuConnection.disconnect();
+ mSortMenuConnection.disconnect();
+ mTrashMenuConnection.disconnect();
// FIRE-17626: Attachment count in appearance floater
if (gInventory.containsObserver(mCategoriesObserver))
@@ -315,6 +324,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");
@@ -341,6 +366,14 @@ void LLPanelOutfitsInventory::onTrashButtonClick()
}
}
+void LLPanelOutfitsInventory::onGearMouseDown()
+{
+ if (mActivePanel)
+ {
+ mActivePanel->updateMenuItemsVisibility();
+ }
+}
+
bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata)
{
return mActivePanel && mActivePanel->isActionEnabled(userdata);
@@ -377,6 +410,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 e8b2221874..adeed9d28d 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;
@@ -72,6 +74,13 @@ public:
bool isCOFPanelActive() const;
+ void setMenuButtons(
+ LLMenuButton* gear_menu,
+ LLMenuButton* sort_menu,
+ LLButton* trash_btn,
+ LLPanel* sort_menu_panel,
+ LLPanel* trash_menu_panel);
+
// Show avatar complexity in appearance floater
void updateAvatarComplexity(U32 complexity, const std::map& item_complexity, const std::map& temp_item_complexity, U32 body_parts_complexity);
@@ -113,20 +122,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/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 69fe345e42..a4ca6f6402 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -969,6 +969,10 @@ void LLPanelPeople::updateNearbyList()
}
// [/RLVa:KB]
mNearbyList->setDirty();
+#ifdef LL_DISCORD
+ if (gSavedSettings.getBOOL("EnableDiscord"))
+ LLAppViewer::updateDiscordPartyMaxSize((S32)mNearbyList->getIDs().size());
+#endif
DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
LLActiveSpeakerMgr::instance().update(true);
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index 5feb605073..5e0a290efe 100644
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -177,11 +177,15 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)
{
+ std::string item = userdata.asString();
if(gAgent.getID() == mUUIDs.front())
{
+ if (item == std::string("can_zoom_in"))
+ {
+ return true;
+ }
return false;
}
- std::string item = userdata.asString();
// Note: can_block and can_delete is used only for one person selected menu
// so we don't need to go over all uuids.
@@ -416,7 +420,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 cfe8a21462..485b1ce22b 100644
--- a/indra/newview/llpanelpermissions.cpp
+++ b/indra/newview/llpanelpermissions.cpp
@@ -213,7 +213,7 @@ bool LLPanelPermissions::postBuild()
getChild("sale type")->setCommitCallback( boost::bind(&LLPanelPermissions::onCommitSaleInfo, this));
- getChild("Edit Cost")->setCommitCallback( boost::bind(&LLPanelPermissions::onCommitSaleInfo, this));
+ getChild("Edit Cost")->setCommitCallback( boost::bind(&LLPanelPermissions::onCommitSalePrice, this));
getChild("button mark for sale")->setCommitCallback( boost::bind(&LLPanelPermissions::setAllSaleInfo, this));
@@ -638,7 +638,7 @@ void LLPanelPermissions::refresh()
}
else
{
-// FIRE-777:�allow batch edit for name and description
+// FIRE-777:?allow batch edit for name and description
// getChild("Object Name")->setValue(LLStringUtil::null);
// LineEditorObjectDesc->setText(LLStringUtil::null);
if (keyboard_focus_view != LineEditorObjectName)
@@ -658,7 +658,7 @@ void LLPanelPermissions::refresh()
// figure out the contents of the name, description, & category
bool edit_name_desc = false;
-// FIRE-777:�allow batch edit for name and description
+// FIRE-777:?allow batch edit for name and description
// if (is_one_object && objectp->permModify() && !objectp->isPermanentEnforced())
if (objectp->permModify())
// /FIRE-777
@@ -958,7 +958,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);
@@ -1459,6 +1461,15 @@ void LLPanelPermissions::onCommitSaleInfo()
}
}
+void LLPanelPermissions::onCommitSalePrice()
+{
+ LLCheckBoxCtrl *checkPurchase = getChild("checkbox for sale");
+ if (checkPurchase && checkPurchase->get())
+ {
+ setAllSaleInfo();
+ }
+}
+
void LLPanelPermissions::setAllSaleInfo()
{
LL_INFOS() << "LLPanelPermissions::setAllSaleInfo()" << LL_ENDL;
diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h
index c48a693032..0084aa6ab5 100644
--- a/indra/newview/llpanelpermissions.h
+++ b/indra/newview/llpanelpermissions.h
@@ -79,6 +79,7 @@ protected:
void onCommitForSale();
void onCommitSaleInfo();
+ void onCommitSalePrice();
void setAllSaleInfo();
void showMarkForSale(bool show);
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index cf8cc1cfdb..dd2234cd34 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -2859,6 +2859,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 5653478457..8001c4c6e0 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -127,6 +127,7 @@ protected:
registrar.add("Wearing.TakeOffDetach",
boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs));
// [/SL:KB]
+ registrar.add("Wearing.Favorite", boost::bind(toggle_favorites, mUUIDs));
LLContextMenu* menu = createFromFile("menu_wearing_tab.xml");
updateMenuItemsVisibility(menu);
@@ -139,6 +140,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;
// [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7)
S32 rlv_locked_count = 0;
// [/RLVa:KB]
@@ -154,6 +157,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)
{
@@ -167,6 +173,8 @@ protected:
{
attachments_selected = true;
}
+ can_favorite |= !linked_item->getIsFavorite();
+ can_unfavorite |= linked_item->getIsFavorite();
// [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7)
if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(*it)) )
{
@@ -192,6 +200,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);
// [SL:KB] - Patch: Inventory-AttachmentEdit - Checked: 2010-09-04 (Catznip-2.2.0a) | Added: Catznip-2.1.2a
menu->setItemVisible("take_off_or_detach", (!allow_detach) && (!allow_take_off) && (clothes_selected) && (attachments_selected));
// [/SL:KB]
@@ -269,6 +279,10 @@ LLPanelWearing::~LLPanelWearing()
{
mAttachmentsChangedConnection.disconnect();
}
+ if (mGearMenuConnection.connected())
+ {
+ mGearMenuConnection.disconnect();
+ }
}
bool LLPanelWearing::postBuild()
@@ -291,10 +305,6 @@ bool LLPanelWearing::postBuild()
// Show avatar complexity in appearance floater
mAvatarComplexityLabel = getChild("avatar_complexity_label");
- LLMenuButton* menu_gear_btn = getChild("options_gear_btn");
-
- menu_gear_btn->setMenu(mGearMenu->getMenu());
-
return true;
}
@@ -613,6 +623,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 8b797af275..61e26d49f9 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -85,6 +85,11 @@ public:
void onEditAttachment();
void onRemoveAttachment();
+ void updateMenuItemsVisibility() {};
+ LLToggleableMenu* getGearMenu();
+ LLToggleableMenu* getSortMenu();
+ bool getTrashMenuVisible() { return false; }
+
// Show avatar complexity in appearance floater
void updateAvatarComplexity(U32 complexity, const std::map& item_complexity, const std::map& temp_item_complexity, U32 body_parts_complexity);
@@ -100,6 +105,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 3fb70a25ad..07ea2219d6 100644
--- a/indra/newview/llreflectionmap.h
+++ b/indra/newview/llreflectionmap.h
@@ -131,7 +131,7 @@ public:
LLSpatialGroup* mGroup = nullptr;
// viewer object this probe is tracking (if any)
- LLPointer mViewerObject; // remove initialisation here, leave it to the pointer class and avoid issues
+ 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 98660e9047..a366f5b290 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -275,6 +275,7 @@ LLSelectMgr::LLSelectMgr()
LLSelectMgr::~LLSelectMgr()
{
clearSelections();
+ mSlectionLodModChangedConnection.disconnect();
}
void LLSelectMgr::clearSelections()
@@ -2394,6 +2395,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*/);
@@ -2404,18 +2406,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 276559613f..9b7e3b3eea 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -1015,6 +1015,7 @@ private:
bool mForceSelection;
std::vector mPauseRequests;
+ boost::signals2::connection mSlectionLodModChangedConnection;
// show/hide build highlight
EFSShowHideHighlight mFSShowHideHighlight;
diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp
index 682e79a578..55b6a859d2 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"
@@ -147,6 +148,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 da78b59ece..f5939bd8a0 100644
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -60,6 +60,8 @@
#include "rlvcommon.h"
// [/RLVa:KB]
+const char* const DEFAULT_DESC = "(No Description)";
+
class PropertiesChangedCallback : public LLInventoryCallback
{
public:
@@ -132,6 +134,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);
@@ -162,10 +165,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
@@ -1000,17 +1004,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 92eda5d47c..0fbb0b936c 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
{
@@ -109,6 +110,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 5b2fbe7bf7..49656fed28 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/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index c4dad3d4eb..619b7f02e1 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -1106,7 +1106,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/llspeakers.cpp b/indra/newview/llspeakers.cpp
index 889d6e60e7..c0aa7db2ca 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -1043,6 +1043,10 @@ void LLLocalSpeakerMgr::updateSpeakerList()
//LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), (F32)LFSimFeatureHandler::getInstance()->sayRange());
// Opensim
+#ifdef LL_DISCORD
+ if (gSavedSettings.getBOOL("EnableDiscord"))
+ LLAppViewer::updateDiscordPartyCurrentSize((S32)avatar_ids.size());
+#endif
for(U32 i=0; isetRebakeStuck(true);
+ mBar->setRebakeStuck(true);
return true;
}
+
private:
- LLStatusBar *mbar;
+ LLStatusBar* mBar;
};
//
@@ -183,6 +185,8 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
mBtnVolume(NULL),
mBoxBalance(NULL),
mBalance(0),
+ mBalanceClicked(false),
+ mObscureBalance(false),
mHealth(100),
mShowParcelIcons(true),
mSquareMetersCredit(0),
@@ -207,9 +211,6 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
// status bar can possible overlay menus?
setMouseOpaque(false);
- mBalanceTimer = new LLFrameTimer();
- mHealthTimer = new LLFrameTimer();
-
LLUICtrl::CommitCallbackRegistry::currentRegistrar()
.add("TopInfoBar.Action", boost::bind(&LLStatusBar::onContextMenuItemClicked, this, _2));
@@ -223,12 +224,6 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
LLStatusBar::~LLStatusBar()
{
- delete mBalanceTimer;
- mBalanceTimer = NULL;
-
- delete mHealthTimer;
- mHealthTimer = NULL;
-
if (mParcelChangedObserver)
{
LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangedObserver);
@@ -304,7 +299,8 @@ bool LLStatusBar::postBuild()
//getChild("goShop")->setCommitCallback(boost::bind(&LLWeb::loadURL, gSavedSettings.getString("MarketplaceURL"), LLStringUtil::null, LLStringUtil::null));
mBoxBalance = getChild("balance");
- mBoxBalance->setClickedCallback( &LLStatusBar::onClickBalance, this );
+ mBoxBalance->setClickedCallback(&LLStatusBar::onClickRefreshBalance, this);
+ //mBoxBalance->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onClickToggleBalance(); }); // Prefer custom FS balance hiding method
mIconPresetsCamera = getChild( "presets_icon_camera" );
//mIconPresetsCamera->setMouseEnterCallback(boost::bind(&LLStatusBar::mIconPresetsCamera, this));
@@ -354,6 +350,8 @@ bool LLStatusBar::postBuild()
gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&LLStatusBar::onVolumeChanged, this, _2));
// Fix LL voice disabled on 2nd instance nonsense
//gSavedSettings.getControl("EnableVoiceChat")->getSignal()->connect(boost::bind(&LLStatusBar::onVoiceChanged, this, _2));
+ // Prefer custom FS balance hiding method
+ //gSavedSettings.getControl("ObscureBalanceInStatusBar")->getSignal()->connect(boost::bind(&LLStatusBar::onObscureBalanceChanged, this, _2));
//if (!gSavedSettings.getBOOL("EnableVoiceChat") && LLAppViewer::instance()->isSecondInstance())
//{
@@ -361,6 +359,7 @@ bool LLStatusBar::postBuild()
// mBtnVolume->setImageUnselected(LLUI::getUIImage("VoiceMute_Off"));
//}
//
+ //mObscureBalance = gSavedSettings.getBOOL("ObscureBalanceInStatusBar"); // Prefer custom FS balance hiding method
// FIRE-19697: Add setting to disable graphics preset menu popup on mouse over
gSavedSettings.getControl("FSStatusBarMenuButtonPopupOnRollover")->getSignal()->connect(boost::bind(&LLStatusBar::onPopupRolloverChanged, this, _2));
@@ -686,6 +685,12 @@ void LLStatusBar::refresh()
//
}
+ if (mBalanceClicked && mBalanceClickTimer.getElapsedTimeF32() > 1.f)
+ {
+ mBalanceClicked = false;
+ sendMoneyBalanceRequest();
+ }
+
// Pathfinding rebake functions
LLMenuOptionPathfindingRebakeNavmesh& navmesh = LLMenuOptionPathfindingRebakeNavmesh::instance();
static LLMenuOptionPathfindingRebakeNavmesh::ERebakeNavMeshMode pathfinding_mode = LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Default;
@@ -829,9 +834,17 @@ void LLStatusBar::setBalance(S32 balance)
std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance );
LLStringUtil::format_map_t string_args;
- string_args["[AMT]"] = llformat("%s", money_str.c_str());
+ if (mObscureBalance)
+ {
+ string_args["[AMT]"] = "****";
+ }
+ else
+ {
+ string_args["[AMT]"] = llformat("%s", money_str.c_str());
+ }
std::string label_str = getString("buycurrencylabel", string_args);
mBoxBalance->setValue(label_str);
+ mBoxBalance->setToolTipArg(LLStringExplicit("[AMT]"), llformat("%s", money_str.c_str()));
updateBalancePanelPosition();
@@ -851,8 +864,6 @@ void LLStatusBar::setBalance(S32 balance)
if( balance != mBalance )
{
- mBalanceTimer->reset();
- mBalanceTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
mBalance = balance;
}
}
@@ -904,9 +915,6 @@ void LLStatusBar::setHealth(S32 health)
}
}
}
-
- mHealthTimer->reset();
- mHealthTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
}
mHealth = health;
@@ -1089,13 +1097,27 @@ void LLStatusBar::onClickVolume(void* data)
}
//static
-void LLStatusBar::onClickBalance(void* )
+void LLStatusBar::onClickRefreshBalance(void* data)
{
- // Force a balance request message:
- LLStatusBar::sendMoneyBalanceRequest();
+ LLStatusBar* status_bar = (LLStatusBar*)data;
+
+ if (!status_bar->mBalanceClicked)
+ {
+ // Schedule a balance request message:
+ status_bar->mBalanceClicked = true;
+ status_bar->mBalanceClickTimer.reset();
+ }
// The refresh of the display (call to setBalance()) will be done by process_money_balance_reply()
}
+void LLStatusBar::onClickToggleBalance()
+{
+ mObscureBalance = !mObscureBalance;
+ gSavedSettings.setBOOL("ObscureBalanceInStatusBar", mObscureBalance);
+ setBalance(mBalance);
+ mBalanceClicked = false; // supress click
+}
+
//static
void LLStatusBar::onClickMediaToggle(void* data)
{
@@ -1200,6 +1222,12 @@ void LLStatusBar::onVolumeChanged(const LLSD& newvalue)
//}
//
+void LLStatusBar::onObscureBalanceChanged(const LLSD& newvalue)
+{
+ mObscureBalance = newvalue.asBoolean();
+ setBalance(mBalance);
+}
+
void LLStatusBar::onUpdateFilterTerm()
{
LLWString searchValue = utf8str_to_wstring( mFilterEdit->getValue() );
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index 91a33ca9a4..248e4eea6a 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -113,7 +113,8 @@ public:
void debitBalance(S32 debit);
void creditBalance(S32 credit);
- // Request the latest currency balance from the server
+ // Request the latest currency balance from the server.
+ // Reply at process_money_balance_reply()
static void sendMoneyBalanceRequest();
void setHealth(S32 percent);
@@ -165,6 +166,7 @@ private:
void onClickBuyCurrency();
void onVolumeChanged(const LLSD& newvalue);
//void onVoiceChanged(const LLSD& newvalue); // Fix LL voice disabled on 2nd instance nonsense
+ void onObscureBalanceChanged(const LLSD& newvalue);
void onMouseEnterPresetsCamera();
void onMouseEnterPresets();
@@ -175,7 +177,8 @@ private:
static void onClickMediaToggle(void* data);
static void onClickVolume(void* data); // Open popup panels on click if FSStatusBarMenuButtonPopupOnRollover is disabled
- static void onClickBalance(void* data);
+ static void onClickRefreshBalance(void* data);
+ void onClickToggleBalance();
LLSearchEditor *mFilterEdit;
LLPanel *mSearchPanel;
@@ -343,13 +346,14 @@ private:
S32 mVolumeIconsWidth; // Option to hide volume controls (sounds, media, stream) in upper right
S32 mBalance;
+ bool mBalanceClicked;
+ bool mObscureBalance;
+ LLTimer mBalanceClickTimer;
S32 mHealth;
S32 mSquareMetersCredit;
S32 mSquareMetersCommitted;
bool mAudioStreamEnabled;
bool mShowParcelIcons;
- LLFrameTimer* mBalanceTimer;
- LLFrameTimer* mHealthTimer;
LLPanelPresetsCameraPulldown* mPanelPresetsCameraPulldown;
LLPanelPresetsPulldown* mPanelPresetsPulldown;
LLPanelVolumePulldown* mPanelVolumePulldown;
diff --git a/indra/newview/llteleporthistorystorage.cpp b/indra/newview/llteleporthistorystorage.cpp
index 53d0d2f40a..a73e149649 100644
--- a/indra/newview/llteleporthistorystorage.cpp
+++ b/indra/newview/llteleporthistorystorage.cpp
@@ -144,6 +144,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 221dc573fe..014e9311ac 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -1359,6 +1359,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 d34175dde9..5b2b5ffbd9 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -166,7 +166,6 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(
mTransparentImageAssetID( gSavedSettings.getString( "UIImgTransparentUUID" ) ), // FIRE-5082: "Transparent" button in Texture Panel
mDefaultImageAssetID(default_image_asset_id),
mBlankImageAssetID(blank_image_asset_id),
- mTentative(tentative),
mAllowNoTexture(allow_no_texture),
mLabel(label),
mTentativeLabel(NULL),
@@ -188,8 +187,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);
@@ -201,7 +202,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?
@@ -315,6 +316,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 )
@@ -576,6 +578,8 @@ bool LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask)
void LLFloaterTexturePicker::onOpen(const LLSD& key)
{
if (sLastPickerMode != 0
+ && (mLocalTextureEnabled || sLastPickerMode != 1)
+ && (mBakeTextureEnabled || sLastPickerMode != 2)
// FIRE-30431: Keep radio button mode selection in texture selection
//&& mModeSelector->selectByValue(sLastPickerMode))
&& mModeSelector->setSelectedIndex(sLastPickerMode))
@@ -720,7 +724,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());
@@ -785,10 +789,10 @@ void LLFloaterTexturePicker::draw()
mTentativeLabel->setVisible( false );
}
- mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
- mBlankBtn->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || mTentative);
- mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
- mTransparentBtn->setEnabled((mImageAssetID != mTransparentImageAssetID && mTransparentImageAssetID.notNull()) || mTentative); // FIRE-5082: "Transparent" button in Texture Panel
+ mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || getTentative());
+ mBlankBtn->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || getTentative());
+ mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || getTentative()));
+ mTransparentBtn->setEnabled((mImageAssetID != mTransparentImageAssetID && mTransparentImageAssetID.notNull()) || getTentative()); // FIRE-5082: "Transparent" button in Texture Panel
LLFloater::draw();
@@ -841,7 +845,7 @@ void LLFloaterTexturePicker::draw()
}
// Draw Tentative Label over the image
- if( mTentative && !mViewModel->isDirty() )
+ if( getTentative() && !mViewModel->isDirty() )
{
mTentativeLabel->setVisible( true );
drawChild(mTentativeLabel);
@@ -1047,6 +1051,7 @@ void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
if (self->mOwner)
{
self->setImageID( self->getDefaultImageAssetID() );
+ self->setTentative(false);
}
self->commitIfImmediateSet();
}
@@ -1057,6 +1062,7 @@ void LLFloaterTexturePicker::onBtnBlank(void* userdata)
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
self->setCanApply(true, true);
self->setImageID( self->getBlankImageAssetID() );
+ self->setTentative(false);
self->commitIfImmediateSet();
}
@@ -1077,20 +1083,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();
-}*/
// UUID texture picker
// static
@@ -1342,6 +1338,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
//if (self->mSetImageAssetIDCallback)
//{
// self->mSetImageAssetIDCallback(inworld_id);
+ // self->setTentative(false);
//}
self->setImageID(inworld_id);
//
@@ -1424,6 +1421,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)
@@ -1444,7 +1442,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);
@@ -1621,9 +1619,18 @@ void LLFloaterTexturePicker::refreshInventoryFilter()
void LLFloaterTexturePicker::setLocalTextureEnabled(bool enabled)
{
+ mLocalTextureEnabled = enabled;
// FIRE-30431: Keep radio button mode selection in texture selection
//mModeSelector->setEnabledByValue(1, enabled);
- mModeSelector->setIndexEnabled(1,enabled);}
+ mModeSelector->setIndexEnabled(1,enabled);
+ if (!enabled && (mModeSelector->getValue().asInteger() == 2))
+ {
+ // FIRE-30431: Keep radio button mode selection in texture selection
+ //mModeSelector->selectByValue(0);
+ mModeSelector->setSelectedIndex(0);
+ onModeSelect(0, this);
+ }
+}
void LLFloaterTexturePicker::setBakeTextureEnabled(bool enabled)
{
@@ -1768,6 +1775,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te )
setCanApply(true, true);
//
setImageID(te.getID());
+ setTentative(false);
}
mNoCopyTextureSelected = false;
@@ -2011,6 +2019,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;
@@ -2058,11 +2077,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);
}
@@ -2072,12 +2089,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);
@@ -2269,7 +2280,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)
@@ -2294,6 +2315,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 ad8461fbb1..82e7c7367a 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -175,6 +175,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;
@@ -189,7 +191,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; }
@@ -438,7 +440,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;
@@ -496,6 +497,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 f2c0b8b821..334a62c02c 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -2755,7 +2755,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)
@@ -2767,13 +2767,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;
@@ -2809,7 +2809,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
@@ -2867,12 +2867,12 @@ 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;
}
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index b0fb7025ce..64fe73179a 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -85,6 +85,7 @@ 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);
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 1c08f60e91..77b79b04b6 100644
--- a/indra/newview/lltoast.cpp
+++ b/indra/newview/lltoast.cpp
@@ -443,7 +443,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 8e2b71deba..14316f7f7e 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1139,28 +1139,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);
}
@@ -2240,7 +2245,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));
// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2010-09-28 (Catznip-2.2)
// Make this behave consistent with dad3dWearItem
LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, fReplace));
@@ -2259,7 +2264,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
// Make this behave consistent with dad3dWearItem
rez_attachment(item, 0, !(mask & MASK_CONTROL));
// [/SL:KB]
-// 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 ee42b5c8fc..32ab1272d0 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -1985,7 +1985,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 b95073378a..a01afff62e 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -68,7 +68,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),
@@ -81,7 +82,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(LLTransactionID transactId,
mEveryonePerms(everyonePerms),
mExpectedUploadCost(expectedCost),
mShowInventory(showInventory),
- mFolderId(LLUUID::null),
+ mFolderId(destFolderId),
mItemId(LLUUID::null),
mAssetId(LLAssetID::null)
{ }
@@ -90,7 +91,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),
@@ -103,7 +105,7 @@ LLResourceUploadInfo::LLResourceUploadInfo(std::string name,
mShowInventory(showInventory),
mTransactionId(),
mAssetType(LLAssetType::AT_NONE),
- mFolderId(LLUUID::null),
+ mFolderId(destFolderId),
mItemId(LLUUID::null),
mAssetId(LLAssetID::null)
{
@@ -381,10 +383,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)
{
@@ -609,12 +613,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 8c220f8b2e..9dc1fea6ab 100644
--- a/indra/newview/llviewerattachmenu.cpp
+++ b/indra/newview/llviewerattachmenu.cpp
@@ -132,15 +132,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));
-// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2013-02-04 (Catznip-3.4)
LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, attachmentp, false));
-// [/SL;KB]
copy_inventory_item(gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index aa50a8f6be..963de67bdf 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -86,18 +86,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;
@@ -107,30 +105,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);
@@ -143,6 +160,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()));
}
@@ -150,7 +169,6 @@ const LLMatrix4 &LLViewerCamera::getProjection() const
{
calcProjection(getFar());
return mProjectionMatrix;
-
}
const LLMatrix4 &LLViewerCamera::getModelview() const
@@ -163,13 +181,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;
@@ -397,7 +414,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/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 9564b6b44b..06ad736893 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -71,11 +71,14 @@
#include "llclipboard.h"
#include "llhttpretrypolicy.h"
#include "llsettingsvo.h"
+#include "llinventorylistener.h"
// [RLVa:KB] - Checked: 2014-11-02 (RLVa-1.4.11)
#include "rlvcommon.h"
// [/RLVa:KB]
#include "llviewernetwork.h"
+LLInventoryListener sInventoryListener;
+
// do-nothing ops for use in callbacks.
void no_op_inventory_func(const LLUUID&) {}
void no_op_llsd_func(const LLSD&) {}
@@ -841,27 +844,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)
@@ -1077,10 +1083,7 @@ void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id)
}
}
-//void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp)
-// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2010-09-28 (Catznip-3.4)
void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace)
-// [/SL:KB]
{
if (inv_item.isNull())
return;
@@ -1088,10 +1091,7 @@ void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachme
LLViewerInventoryItem *item = gInventory.getItem(inv_item);
if (item)
{
-// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2010-09-28 (Catznip-3.4)
rez_attachment(item, attachmentp, replace);
-// [/SL:KB]
-// rez_attachment(item, attachmentp);
}
}
@@ -1659,7 +1659,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 8915f24d9a..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,10 +264,7 @@ public:
class LLViewerJointAttachment;
-// [SL:KB] - Patch: Appearance-DnDWear | Checked: 2010-09-28 (Catznip-3.4)
void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace);
-// [/SL:KB]
-//void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp);
void activate_gesture_cb(const LLUUID& inv_item);
diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp
index c8037999bd..151f19daf3 100644
--- a/indra/newview/llviewerjoystick.cpp
+++ b/indra/newview/llviewerjoystick.cpp
@@ -933,6 +933,10 @@ void LLViewerJoystick::moveObjects(bool reset)
{
gAgent.clearAFK();
}
+ else
+ {
+ gAwayTriggerTimer.reset();
+ }
if (sDelta[0] || sDelta[1] || sDelta[2])
{
@@ -1108,6 +1112,10 @@ void LLViewerJoystick::moveAvatar(bool reset)
{
gAgent.clearAFK();
}
+ else
+ {
+ gAwayTriggerTimer.reset();
+ }
setCameraNeedsUpdate(true);
}
@@ -1321,10 +1329,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;
@@ -1387,6 +1402,10 @@ bool LLViewerJoystick::toggleFlycam()
{
gAgent.clearAFK();
}
+ else
+ {
+ gAwayTriggerTimer.reset();
+ }
mOverrideCamera = !mOverrideCamera;
if (mOverrideCamera)
diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp
index 90f0acafb7..d9f3ea60df 100644
--- a/indra/newview/llviewermedia_streamingaudio.cpp
+++ b/indra/newview/llviewermedia_streamingaudio.cpp
@@ -60,13 +60,26 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url)
if(!mMediaPlugin)
return;
- if (!url.empty()) {
+ if (!url.empty())
+ {
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
- mURL = url;
- mMediaPlugin->loadURI ( url );
+
+ mURL = url; // keep original url here for comparison purposes
+ std::string snt_url = url;
+ LLStringUtil::trim(snt_url);
+ size_t pos = snt_url.find(' ');
+ if (pos != std::string::npos)
+ {
+ // fmod permited having names after the url and people were using it.
+ // People label their streams this way, ignore the 'label'.
+ snt_url = snt_url.substr(0, pos);
+ }
+ mMediaPlugin->loadURI(snt_url);
mMediaPlugin->start();
LL_INFOS() << "Playing stream..." << LL_ENDL;
- } else {
+ }
+ else
+ {
LL_INFOS() << "setting stream to NULL"<< LL_ENDL;
mURL.clear();
mMediaPlugin->stop();
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 1803631ddb..d31df58e99 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -89,6 +89,7 @@
#include "lltoolface.h"
#include "llhints.h"
#include "llhudeffecttrail.h"
+#include "llhudeffectresetskeleton.h"
#include "llhudmanager.h"
#include "llimview.h"
#include "llinventorybridge.h"
@@ -2075,7 +2076,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)
@@ -2102,7 +2102,7 @@ class LLAdvancedAppearanceToXML : public view_listener_t
}
if (avatar)
{
- avatar->dumpArchetypeXML(emptyname);
+ avatar->dumpArchetypeXML(LLStringUtil::null);
}
return true;
}
@@ -6936,6 +6936,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
{
@@ -8232,8 +8264,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;
}
};
@@ -8256,8 +8298,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;
}
};
@@ -8284,11 +8336,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;
}
@@ -10887,6 +10952,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)
@@ -12636,6 +12731,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");
@@ -12672,6 +12768,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/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 356ed17a25..80c9ef871a 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -69,6 +69,7 @@
#include "llviewerassetupload.h"
// linden libraries
+#include "llfilesystem.h"
#include "llnotificationsutil.h"
#include "llsdserialize.h"
#include "llsdutil.h"
@@ -486,13 +487,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
@@ -508,12 +515,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)
{
@@ -521,11 +528,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);
}
}
// Import Linkset
@@ -559,16 +566,9 @@ void do_bulk_upload(std::vector filenames, bool allow_2k)
if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))
{
bool resource_upload = false;
- if (asset_type == LLAssetType::AT_TEXTURE && allow_2k)
+ if (asset_type == LLAssetType::AT_TEXTURE)
{
- LLPointer image_frmted = LLImageFormatted::createFromType(codec);
- if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename))
- {
- S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
- S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
- expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height);
- resource_upload = true;
- }
+ resource_upload = true;
}
else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost))
{
@@ -577,23 +577,115 @@ void do_bulk_upload(std::vector filenames, bool allow_2k)
if (resource_upload)
{
- LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo(
- filename,
- asset_name,
- asset_name, 0,
- LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
- LLFloaterPerms::getNextOwnerPerms("Uploads"),
- LLFloaterPerms::getGroupPerms("Uploads"),
- LLFloaterPerms::getEveryonePerms("Uploads"),
- expected_upload_cost);
-
- if (!allow_2k)
+ if (asset_type == LLAssetType::AT_TEXTURE)
{
- info_p->setMaxImageSize(1024);
- }
- LLResourceUploadInfo::ptr_t uploadInfo(info_p);
+ std::string exten = gDirUtilp->getExtension(filename);
+ U32 codec = LLImageBase::getCodecFromExtension(exten);
- upload_new_resource(uploadInfo);
+ // Load the image
+ LLPointer image = LLImageFormatted::createFromType(codec);
+ if (image.isNull())
+ {
+ LL_WARNS() << "Failed to create image container for " << filename << LL_ENDL;
+ continue;
+ }
+ if (!image->load(filename))
+ {
+ LL_WARNS() << "Failed to load image: " << filename << LL_ENDL;
+ continue;
+ }
+ // Decompress or expand it in a raw image structure
+ LLPointer raw_image = new LLImageRaw;
+ if (!image->decode(raw_image, 0.0f))
+ {
+ LL_WARNS() << "Failed to decode image: " << filename << LL_ENDL;
+ continue;
+ }
+ // Check the image constraints
+ if ((image->getComponents() != 3) && (image->getComponents() != 4))
+ {
+ LL_WARNS() << "Attempted to upload a texture that has " << image->getComponents()
+ << " components, but only 3 (RGB) or 4 (RGBA) are allowed." << LL_ENDL;
+ continue;
+ }
+ // Downscale images to fit the max_texture_dimensions_*, or 1024 if allow_2k is false
+ S32 max_width = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024;
+ S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024;
+
+ S32 orig_width = raw_image->getWidth();
+ S32 orig_height = raw_image->getHeight();
+
+ if (orig_width > max_width || orig_height > max_height)
+ {
+ // Calculate scale factors
+ F32 width_scale = (F32)max_width / (F32)orig_width;
+ F32 height_scale = (F32)max_height / (F32)orig_height;
+ F32 scale = llmin(width_scale, height_scale);
+
+ // Calculate new dimensions, preserving aspect ratio
+ S32 new_width = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width));
+ S32 new_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height));
+
+ if (!raw_image->scale(new_width, new_height))
+ {
+ LL_WARNS() << "Failed to scale image from " << orig_width << "x" << orig_height << " to " << new_width << "x"
+ << new_height << LL_ENDL;
+ continue;
+ }
+
+ // Inform the resident about the resized image
+ LLSD subs;
+ subs["[ORIGINAL_WIDTH]"] = orig_width;
+ subs["[ORIGINAL_HEIGHT]"] = orig_height;
+ subs["[NEW_WIDTH]"] = new_width;
+ subs["[NEW_HEIGHT]"] = new_height;
+ subs["[MAX_WIDTH]"] = max_width;
+ subs["[MAX_HEIGHT]"] = max_height;
+ LLNotificationsUtil::add("ImageUploadResized", subs);
+ }
+
+ raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer formatted = new LLImageJ2C;
+
+ if (formatted->encode(raw_image, 0.0f))
+ {
+ LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
+ fmt_file.write(formatted->getData(), formatted->getDataSize());
+
+ LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo(
+ tid, LLAssetType::AT_TEXTURE,
+ asset_name,
+ asset_name, 0,
+ LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ LLAgentBenefitsMgr::current().getTextureUploadCost(raw_image->getWidth(), raw_image->getHeight())
+ ));
+
+ upload_new_resource(assetUploadInfo);
+ }
+ }
+ else
+ {
+ LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo(
+ filename,
+ asset_name,
+ asset_name, 0,
+ LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost);
+ LLResourceUploadInfo::ptr_t uploadInfo(info_p);
+
+ upload_new_resource(uploadInfo);
+ }
}
}
@@ -662,8 +754,31 @@ bool get_bulk_upload_expected_cost(
LLPointer image_frmted = LLImageFormatted::createFromType(codec);
if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename))
{
- S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
- S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+ S32 biased_width, biased_height;
+
+ S32 max_width = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024;
+ S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024;
+
+ S32 orig_width = image_frmted->getWidth();
+ S32 orig_height = image_frmted->getHeight();
+
+ if (orig_width > max_width || orig_height > max_height)
+ {
+ // Calculate scale factors
+ F32 width_scale = (F32)max_width / (F32)orig_width;
+ F32 height_scale = (F32)max_height / (F32)orig_height;
+ F32 scale = llmin(width_scale, height_scale);
+
+ // Calculate new dimensions, preserving aspect ratio
+ biased_width = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width));
+ biased_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height));
+ }
+ else
+ {
+ biased_width = LLImageRaw::biasedDimToPowerOfTwo(orig_width, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+ biased_height = LLImageRaw::biasedDimToPowerOfTwo(orig_height, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+ }
+
total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height);
S32 area = biased_width * biased_height;
if (area >= LLAgentBenefits::MIN_2K_TEXTURE_AREA)
@@ -724,7 +839,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;
@@ -736,6 +851,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;
// Check balance before trying to upload
S32 current_balance = gStatusBar->getBalance();
@@ -782,7 +898,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
@@ -804,7 +920,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
@@ -815,7 +931,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;
}
};
@@ -846,7 +962,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;
}
};
@@ -859,7 +975,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;
}
};
@@ -872,7 +988,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;
}
};
@@ -886,7 +1002,7 @@ class FSFileImportLinkset : public view_listener_t
{
gAgentCamera.changeCameraToDefault();
}
- LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2), LLFilePicker::FFLOAD_IMPORT, false);
+ LLFilePickerReplyThread::startPicker(boost::bind(&upload_single_file, _1, _2, LLUUID::null), LLFilePicker::FFLOAD_IMPORT, false);
return true;
}
};
@@ -1218,7 +1334,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 62a2bf779b..5caf504127 100644
--- a/indra/newview/llviewermenufile.h
+++ b/indra/newview/llviewermenufile.h
@@ -75,6 +75,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/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index bf9b3c3ca3..5e7a36fd3c 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -3929,6 +3929,11 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
}
}
+#ifdef LL_DISCORD
+ if (gSavedSettings.getBOOL("EnableDiscord"))
+ LLAppViewer::updateDiscordActivity();
+#endif
+
if ( LLTracker::isTracking(NULL) )
{
// Check distance to beacon, if < 5m, remove beacon
@@ -4109,6 +4114,7 @@ void send_agent_update(bool force_send, bool send_reliable)
static F64 last_send_time = 0.0;
static U32 last_control_flags = 0;
+ static bool control_flags_follow_up = false;
static U8 last_render_state = 0;
static U8 last_flags = AU_FLAGS_NONE;
static LLQuaternion last_body_rot,
@@ -4186,6 +4192,20 @@ void send_agent_update(bool force_send, bool send_reliable)
break;
}
+ // example:
+ // user taps crouch (control_flags 4128), viewer sends 4128 then immediately 0
+ // server starts crouching motion but does not stop it, only once viewer sends 0
+ // second time will server stop the motion. follow_up exists to make sure all
+ // states like 'crouch' motion are properly cleared server side.
+ //
+ // P.S. Server probably shouldn't require a reminder to stop a motion,
+ // but at the moment it does.
+ if (control_flags_follow_up)
+ {
+ send_update = true;
+ break;
+ }
+
// check translation
constexpr F32 TRANSLATE_THRESHOLD = 0.01f;
if ((last_camera_pos_agent - camera_pos_agent).magVec() > TRANSLATE_THRESHOLD)
@@ -4319,6 +4339,7 @@ void send_agent_update(bool force_send, bool send_reliable)
// remember last update data
last_send_time = now;
+ control_flags_follow_up = last_control_flags != control_flags;
last_control_flags = control_flags;
last_render_state = render_state;
last_flags = flags;
@@ -4605,7 +4626,7 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data)
gObjectList.killObject(objectp);
}
- if(delete_object)
+ if(delete_object && regionp)
{
regionp->killCacheEntry(local_id);
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index d4f9782fea..615c19799d 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -4342,8 +4342,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 e56ab3fe97..39bdc9c8f6 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -240,6 +240,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");
@@ -328,6 +330,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())
@@ -366,6 +370,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);
@@ -654,6 +679,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 ce4f8ffb01..1c47b2fdde 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -276,6 +276,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;
@@ -291,6 +295,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 098f88c16b..2d58f624e3 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1144,6 +1144,7 @@ void LLViewerFetchedTexture::init(bool firstinit)
mOrigHeight = 0;
mHasAux = false;
mNeedsAux = false;
+ mLastWorkerDiscardLevel = -1;
mRequestedDiscardLevel = -1;
mRequestedDownloadPriority = 0.f;
mFullyLoaded = false;
@@ -1296,12 +1297,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);
}
}
@@ -2204,6 +2204,7 @@ bool LLViewerFetchedTexture::updateFetch()
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);
@@ -2849,11 +2850,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 10a79515e9..a9c108f523 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -477,6 +477,7 @@ protected:
bool mKnownDrawSizeChanged ;
std::string mUrl;
+ S32 mLastWorkerDiscardLevel;
S32 mRequestedDiscardLevel;
F32 mRequestedDownloadPriority;
S32 mFetchState;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 529909d086..006eafdb89 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1517,11 +1517,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)
@@ -1640,6 +1646,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
@@ -3410,7 +3420,8 @@ bool LLViewerWindow::handleKey(KEY key, MASK mask)
// [FS Communication UI]
//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)
@@ -3435,9 +3446,10 @@ bool LLViewerWindow::handleKey(KEY key, MASK mask)
// }
// }
// }
+ LLCachedControl key_move(gSavedSettings, "ArrowKeysAlwaysMove");
if(FSNearbyChat::instance().defaultChatBarHasFocus() &&
(FSNearbyChat::instance().defaultChatBarIsIdle() ||
- gSavedSettings.getBOOL("ArrowKeysAlwaysMove")))
+ key_move()))
{
// let Control-Up and Control-Down through for chat line history,
// Control-Right and Control-Left too for chat line editing
@@ -3452,10 +3464,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 d1dc6302a3..a4238880a8 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -122,20 +122,21 @@
#include
+#include "fsavatarrenderpersistence.h"
#include "fscommon.h"
#include "fsdata.h"
+#include "fsdiscordconnect.h" // tapping a place that happens on landing in world to start up discord
+#include "fslslbridge.h" // Movelock position refresh
#include "lfsimfeaturehandler.h" // Opensim
#include "lggcontactsets.h"
#include "llcontrol.h"
#include "llfilepicker.h" // FIRE-8893 - Dump archetype xml to user defined location
-#include "llviewermenufile.h"
+#include "llhudeffectresetskeleton.h"
#include "llnetmap.h"
-#include "llviewernetwork.h" // [FS:CR] isInSecondlife()
#include "llsidepanelappearance.h"
-#include "fsavatarrenderpersistence.h"
-#include "fslslbridge.h" // Movelock position refresh
+#include "llviewermenufile.h"
+#include "llviewernetwork.h" // [FS:CR] isInSecondlife()
-#include "fsdiscordconnect.h" // tapping a place that happens on landing in world to start up discord
extern F32 SPEED_ADJUST_MAX;
extern F32 SPEED_ADJUST_MAX_SEC;
@@ -607,7 +608,7 @@ private:
// joint states to be animated
//-------------------------------------------------------------------------
LLPointer mPelvisState;
- LLCharacter* mCharacter;
+ LLCharacter* mCharacter;
};
/**
@@ -720,11 +721,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mVisuallyMuteSetting(AV_RENDER_NORMALLY),
mMutedAVColor(LLColor4::white /* used for "uninitialize" */),
mFirstFullyVisible(true),
+ mWaitingForMeshes(false),
mFirstDecloudTime(-1.f),
mFullyLoaded(false),
mPreviousFullyLoaded(false),
mFullyLoadedInitialized(false),
mLastCloudAttachmentCount(-1),
+ mFullyLoadedFrameCounter(0),
mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),
mLoadedCallbacksPaused(false),
mLoadedCallbackTextures(0),
@@ -864,11 +867,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)
@@ -891,10 +892,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);
}
}
@@ -907,14 +908,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;
@@ -1011,12 +1012,12 @@ bool LLVOAvatar::isFullyTextured() const
bool LLVOAvatar::hasGray() const
{
- return !getIsCloud() && !isFullyTextured();
+ return !getHasMissingParts() && !isFullyTextured();
}
S32 LLVOAvatar::getRezzedStatus() const
{
- if (getIsCloud()) return 0;
+ if (getHasMissingParts()) return 0;
bool textured = isFullyTextured();
bool all_baked_loaded = allBakedTexturesCompletelyDownloaded();
if (textured && all_baked_loaded && getAttachmentCount() == mSimAttachments.size()) return 4;
@@ -1059,34 +1060,49 @@ bool LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars)
++grey_avatars;
}
}
- return !grey_avatars;
+ return grey_avatars == 0;
}
// static
-void LLVOAvatar::getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars)
+void LLVOAvatar::getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)
{
counts.clear();
counts.resize(5);
avg_cloud_time = 0;
cloud_avatars = 0;
+ pending_meshes = 0;
+ control_avatars = 0;
S32 count_avg = 0;
for (LLCharacter* character : LLCharacter::sInstances)
{
- if (LLVOAvatar* inst = (LLVOAvatar*)character)
+ LLVOAvatar* inst = (LLVOAvatar*)character;
+ if (inst && !inst->isUIAvatar() && !inst->isSelf())
{
- S32 rez_status = inst->getRezzedStatus();
- counts[rez_status]++;
- F32 time = inst->getFirstDecloudTime();
- if (time >= 0)
+ if (inst->isControlAvatar())
{
- avg_cloud_time+=time;
- count_avg++;
+ control_avatars++;
}
- if (!inst->isFullyLoaded() || time < 0)
+ else
{
- // still renders as cloud
- cloud_avatars++;
+ S32 rez_status = inst->getRezzedStatus();
+ counts[rez_status]++;
+ F32 time = inst->getFirstDecloudTime();
+ if (time >= 0)
+ {
+ avg_cloud_time += time;
+ count_avg++;
+ }
+ if (!inst->isFullyLoaded() || time < 0)
+ {
+ // still renders as cloud
+ cloud_avatars++;
+ if (rez_status >= 4
+ && inst->mWaitingForMeshes)
+ {
+ pending_meshes++;
+ }
+ }
}
}
}
@@ -1103,7 +1119,7 @@ std::string LLVOAvatar::rezStatusToString(S32 rez_status)
switch (rez_status)
{
case 0:
- return "cloud";
+ return "missing parts";
case 1:
return "gray";
case 2:
@@ -3369,7 +3385,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;
@@ -3606,17 +3622,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());
@@ -3626,7 +3642,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)
mOohMorph->setWeight( ooh_weight, false);
}
- if( mAahMorph )
+ if (mAahMorph)
{
F32 aah_weight = mAahMorph->getMinWeight()
+ aah_morph_amount * (mAahMorph->getMaxWeight() - mAahMorph->getMinWeight());
@@ -3965,7 +3981,7 @@ void LLVOAvatar::idleUpdateNameTagText(bool new_name)
// [RLVa:KB] - Checked: RLVa-1.2.2
bool is_friend = fRlvShowAvName && isBuddy();
// [/RLVa:KB]
- bool is_cloud = getIsCloud();
+ bool is_cloud = getHasMissingParts();
if (is_appearance != mNameAppearance)
{
@@ -5165,8 +5181,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;
@@ -5179,14 +5195,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;
}
@@ -5195,7 +5211,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;
}
@@ -7114,8 +7130,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");
@@ -7165,7 +7184,6 @@ LLUUID LLVOAvatar::remapMotionID(const LLUUID& id)
}
return result;
-
}
//-----------------------------------------------------------------------------
@@ -7173,6 +7191,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;
@@ -7219,6 +7238,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;
@@ -7282,15 +7302,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;
@@ -7300,6 +7335,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)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
@@ -7428,6 +7464,7 @@ bool LLVOAvatar::jointIsRiggedTo(const LLJoint *joint) const
void LLVOAvatar::clearAttachmentOverrides()
{
+
for (S32 i=0; i meshes_seen;
@@ -7581,11 +7620,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++)
{
@@ -7818,22 +7857,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)
@@ -8759,102 +8798,100 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
}
}
-// remove mesh rezzing delay
-// //bool check_object_for_mesh_loading(LLViewerObject* objectp)
-//{
-// if (!objectp || !objectp->getVolume())
-// {
-// return false;
-// }
-// LLVolume* volp = objectp->getVolume();
-// const LLUUID& mesh_id = volp->getParams().getSculptID();
-// if (mesh_id.isNull())
-// {
-// // No mesh nor skin info needed
-// return false;
-// }
-//
-// if (volp->isMeshAssetUnavaliable())
-// {
-// // Mesh failed to load, do not expect it
-// return false;
-// }
-//
-// if (!objectp->mDrawable)
-// {
-// return false;
-// }
-//
-// LLVOVolume* pvobj = objectp->mDrawable->getVOVolume();
-// if (pvobj)
-// {
-// if (!pvobj->isMesh())
-// {
-// // Not a mesh
-// return false;
-// }
-//
-// if (!volp->isMeshAssetLoaded())
-// {
-// // Waiting for mesh
-// return true;
-// }
-//
-// const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo();
-// if (skin_data)
-// {
-// // Skin info present, done
-// return false;
-// }
-//
-// if (pvobj->isSkinInfoUnavaliable())
-// {
-// // Load failed or info not present, don't expect it
-// return false;
-// }
-// }
-//
-// // object is not ready
-// return true;
-//}
-//
-//bool LLVOAvatar::hasPendingAttachedMeshes()
-//{
-// for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
-// iter != mAttachmentPoints.end();
-// ++iter)
-// {
-// LLViewerJointAttachment* attachment = iter->second;
-// if (attachment)
-// {
-// for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
-// attachment_iter != attachment->mAttachedObjects.end();
-// ++attachment_iter)
-// {
-// LLViewerObject* objectp = attachment_iter->get();
-// if (objectp && !objectp->isDead())
-// {
-// if (check_object_for_mesh_loading(objectp))
-// {
-// return true;
-// }
-// LLViewerObject::const_child_list_t& child_list = objectp->getChildren();
-// for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin();
-// iter1 != child_list.end(); ++iter1)
-// {
-// LLViewerObject* objectchild = *iter1;
-// if (check_object_for_mesh_loading(objectchild))
-// {
-// return true;
-// }
-// }
-// }
-// }
-// }
-// }
-// return false;
-//}
-//
+bool check_object_for_mesh_loading(LLViewerObject* objectp)
+{
+ if (!objectp || !objectp->getVolume())
+ {
+ return false;
+ }
+ LLVolume* volp = objectp->getVolume();
+ const LLUUID& mesh_id = volp->getParams().getSculptID();
+ if (mesh_id.isNull())
+ {
+ // No mesh nor skin info needed
+ return false;
+ }
+
+ if (volp->isMeshAssetUnavaliable())
+ {
+ // Mesh failed to load, do not expect it
+ return false;
+ }
+
+ if (!objectp->mDrawable)
+ {
+ return false;
+ }
+
+ LLVOVolume* pvobj = objectp->mDrawable->getVOVolume();
+ if (pvobj)
+ {
+ if (!pvobj->isMesh())
+ {
+ // Not a mesh
+ return false;
+ }
+
+ if (!volp->isMeshAssetLoaded())
+ {
+ // Waiting for mesh
+ return true;
+ }
+
+ const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo();
+ if (skin_data)
+ {
+ // Skin info present, done
+ return false;
+ }
+
+ if (pvobj->isSkinInfoUnavaliable())
+ {
+ // Load failed or info not present, don't expect it
+ return false;
+ }
+ }
+
+ // object is not ready
+ return true;
+}
+
+bool LLVOAvatar::hasPendingAttachedMeshes()
+{
+ for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
+ {
+ LLViewerJointAttachment* attachment = iter->second;
+ if (attachment)
+ {
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ LLViewerObject* objectp = attachment_iter->get();
+ if (objectp && !objectp->isDead())
+ {
+ if (check_object_for_mesh_loading(objectp))
+ {
+ return true;
+ }
+ LLViewerObject::const_child_list_t& child_list = objectp->getChildren();
+ for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin();
+ iter1 != child_list.end(); ++iter1)
+ {
+ LLViewerObject* objectchild = *iter1;
+ if (check_object_for_mesh_loading(objectchild))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
//-----------------------------------------------------------------------------
// detachObject()
@@ -9097,6 +9134,16 @@ void LLVOAvatar::getOffObject()
{
revokePermissionsOnObject(sit_object);
}
+
+ // Reset skeleton on stand up option
+ if (gSavedSettings.getBOOL("FSResetSkeletonOnStandUp"))
+ {
+ LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true);
+ effectp->setSourceObject(gAgentAvatarp);
+ effectp->setTargetObject(gAgentAvatarp);
+ effectp->setResetAnimations(false);
+ }
+ //
}
}
@@ -9327,7 +9374,7 @@ bool LLVOAvatar::isVisible() const
}
// Determine if we have enough avatar data to render
-bool LLVOAvatar::getIsCloud() const
+bool LLVOAvatar::getHasMissingParts() const
{
if (mIsDummy)
{
@@ -9345,7 +9392,7 @@ bool LLVOAvatar::getIsCloud() 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.
@@ -9365,7 +9412,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));
}
@@ -9373,7 +9420,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);
@@ -9536,8 +9583,12 @@ bool LLVOAvatar::updateIsFullyLoaded()
|| (mLoadedCallbackTextures < mCallbackTextureList.size() && mLastTexCallbackAddedTime.getElapsedTimeF32() < MAX_TEXTURE_WAIT_TIME_SEC)
|| !mPendingAttachment.empty()
|| (rez_status < 3 && !isFullyBaked())
- // || hasPendingAttachedMeshes() //
);
+ if (!loading)
+ {
+ mWaitingForMeshes = hasPendingAttachedMeshes();
+ loading = mWaitingForMeshes;
+ }
// compare amount of attachments to one reported by simulator
if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0)
@@ -9616,19 +9667,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);
}
@@ -9645,12 +9696,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;
@@ -9668,6 +9719,7 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading)
mNeedsImpostorUpdate = true;
mLastImpostorUpdateReason = 6;
}
+
return changed;
}
@@ -10620,12 +10672,12 @@ void dump_visual_param(LLAPRFile::tFiletype* file, LLVisualParam* viewer_param,
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 );
// Remove LLVolatileAPRPool/apr_file_t and use FILE* instead
@@ -10855,7 +10907,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;
@@ -11550,13 +11602,9 @@ 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");
// FIRE-8893 - Dump archetype xml to user defined location
LLFilePickerReplyThread::startPicker(boost::bind(&LLVOAvatar::dumpArchetypeXMLCallback, this, _1, group_by_wearables),
@@ -11569,7 +11617,7 @@ void LLVOAvatar::dumpArchetypeXMLCallback(const std::vector& filena
LLAPRFile outfile;
LLWearableType *wr_inst = LLWearableType::getInstance();
// FIRE-8893 - Dump archetype xml to user defined location
- //std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
+ //std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, outfilename);
std::string fullpath = filenames[0];
//
if (APR_SUCCESS == outfile.open(fullpath, LL_APR_WB ))
@@ -12061,7 +12109,7 @@ void LLVOAvatar::updateRiggingInfo()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
- //LL_DEBUGS("RigSpammish") << getFullname() << " updating rig tab" << LL_ENDL; // Performance tweak
+ //LL_DEBUGS("RigSpammish") << getDebugName() << " updating rig tab" << LL_ENDL; // Performance tweak
// use a local static for scratch space to avoid reallocation here
static std::vector volumes;
@@ -13146,16 +13194,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 beb6dd05ea..bee29e2339 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -235,6 +235,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);
@@ -414,17 +415,16 @@ public:
virtual bool isTooComplex() const; // FIRE-29012: Standalone animesh avatars get affected by complexity limit; changed to virtual
bool visualParamWeightsAreDefault();
- virtual bool getIsCloud() const;
+ 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 getNumBakes() const;// BOM bake limits
// U8 getNumTEs() const override;// BOM bake limits
S32 mLastRezzedStatus;
-
void startPhase(const std::string& phase_name);
void stopPhase(const std::string& phase_name, bool err_check = true);
void clearPhases();
@@ -443,6 +443,7 @@ protected:
private:
bool mFirstFullyVisible;
+ bool mWaitingForMeshes;
F32 mFirstDecloudTime;
LLFrameTimer mFirstAppearanceMessageTimer;
@@ -754,7 +755,7 @@ public:
bool isFullyBaked();
static bool areAllNearbyInstancesBaked(S32& grey_avatars);
- static void getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars);
+ static void getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars);
static std::string rezStatusToString(S32 status);
//--------------------------------------------------------------------
@@ -980,7 +981,7 @@ public:
virtual bool detachObject(LLViewerObject *viewer_object);
static bool getRiggedMeshID( LLViewerObject* pVO, LLUUID& mesh_id );
void cleanupAttachedMesh( LLViewerObject* pVO );
- // bool hasPendingAttachedMeshes(); // remove mesh rezzing delay
+ bool hasPendingAttachedMeshes();
static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
/*virtual*/ bool isWearingWearableType(LLWearableType::EType type ) const;
LLViewerObject * findAttachmentByID( const LLUUID & target_id ) const;
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 68ca411af0..2b8aef077d 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -2486,7 +2486,7 @@ void LLVOAvatarSelf::dumpTotalLocalTextureByteCount()
LL_INFOS() << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << LL_ENDL;
}
-bool LLVOAvatarSelf::getIsCloud() const
+bool LLVOAvatarSelf::getHasMissingParts() const
{
// Let people know why they're clouded without spamming them into oblivion.
bool do_warn = false;
@@ -2796,14 +2796,18 @@ void LLVOAvatarSelf::appearanceChangeMetricsCoro(std::string url)
std::vector rez_counts;
F32 avg_time;
S32 total_cloud_avatars;
- LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars);
+ S32 waiting_for_meshes;
+ S32 control_avatars;
+ LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars, waiting_for_meshes, control_avatars);
for (S32 rez_stat = 0; rez_stat < rez_counts.size(); ++rez_stat)
{
std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat);
msg["nearby"][rez_status_name] = rez_counts[rez_stat];
}
+ msg["nearby"]["waiting_for_meshes"] = waiting_for_meshes;
msg["nearby"]["avg_decloud_time"] = avg_time;
msg["nearby"]["cloud_total"] = total_cloud_avatars;
+ msg["nearby"]["animeshes"] = control_avatars;
// std::vector bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake");
std::vector by_fields;
@@ -3445,6 +3449,14 @@ void LLVOAvatarSelf::setHoverOffset(const LLVector3& hover_offset, bool send_upd
//------------------------------------------------------------------------
bool LLVOAvatarSelf::needsRenderBeam()
{
+ // Prefer custom FS lookat privacy code
+ //static LLCachedControl enable_selection_hints(gSavedSettings, "EnableSelectionHints", true);
+ //if (!enable_selection_hints)
+ //{
+ // return false;
+ //}
+ //
+
LLTool *tool = LLToolMgr::getInstance()->getCurrentTool();
bool is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing());
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index be35df4bc0..224b15044c 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -144,7 +144,7 @@ public:
// Loading state
//--------------------------------------------------------------------
public:
- /*virtual*/ bool getIsCloud() const;
+ /*virtual*/ bool getHasMissingParts() const;
//--------------------------------------------------------------------
// Region state
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index a7e336febb..0aa7371a1f 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -1921,11 +1921,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 496c00927b..bf1b71825a 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"
@@ -336,35 +337,37 @@ void LLWebRTCVoiceClient::updateSettings()
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled());
- static LLCachedControl sVoiceEarLocation(gSavedSettings, "VoiceEarLocation");
- setEarLocation(sVoiceEarLocation);
+ if (mVoiceEnabled)
+ {
+ static LLCachedControl sVoiceEarLocation(gSavedSettings, "VoiceEarLocation");
+ setEarLocation(sVoiceEarLocation);
- static LLCachedControl sInputDevice(gSavedSettings, "VoiceInputAudioDevice");
- setCaptureDevice(sInputDevice);
+ static LLCachedControl sInputDevice(gSavedSettings, "VoiceInputAudioDevice");
+ setCaptureDevice(sInputDevice);
- static LLCachedControl sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice");
- setRenderDevice(sOutputDevice);
+ static LLCachedControl sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice");
+ setRenderDevice(sOutputDevice);
- LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice()) << LL_ENDL;
+ LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice()) << LL_ENDL;
- static LLCachedControl sMicLevel(gSavedSettings, "AudioLevelMic");
- setMicGain(sMicLevel);
+ static LLCachedControl sMicLevel(gSavedSettings, "AudioLevelMic");
+ setMicGain(sMicLevel);
- llwebrtc::LLWebRTCDeviceInterface::AudioConfig config;
+ llwebrtc::LLWebRTCDeviceInterface::AudioConfig config;
- static LLCachedControl sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true);
- config.mEchoCancellation = sEchoCancellation;
+ static LLCachedControl sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true);
+ config.mEchoCancellation = sEchoCancellation;
- static LLCachedControl sAGC(gSavedSettings, "VoiceAutomaticGainControl", true);
- config.mAGC = sAGC;
+ static LLCachedControl sAGC(gSavedSettings, "VoiceAutomaticGainControl", true);
+ config.mAGC = sAGC;
- static LLCachedControl sNoiseSuppressionLevel(gSavedSettings,
- "VoiceNoiseSuppressionLevel",
- llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH);
- config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel) (U32)sNoiseSuppressionLevel;
-
- mWebRTCDeviceInterface->setAudioConfig(config);
+ static LLCachedControl sNoiseSuppressionLevel(gSavedSettings,
+ "VoiceNoiseSuppressionLevel",
+ llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH);
+ config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel)(U32)sNoiseSuppressionLevel;
+ mWebRTCDeviceInterface->setAudioConfig(config);
+ }
}
// Observers
@@ -2002,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.
@@ -2026,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);
@@ -2035,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();
@@ -2389,6 +2435,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 +2797,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 +2847,10 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
if (primary != mPrimary)
{
mPrimary = primary;
+ if (mWebRTCAudioInterface)
+ {
+ mWebRTCAudioInterface->setMute(mMuted || !mPrimary);
+ }
sendJoin();
}
}
@@ -3097,10 +3153,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 4ce8892a44..f033f1c119 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -382,8 +382,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;
}
@@ -1348,12 +1351,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;
@@ -1641,6 +1647,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
{
@@ -1651,6 +1658,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)
{
@@ -1715,11 +1723,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);
@@ -3784,12 +3797,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 8b1497ebb8..ec4beff41f 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -191,6 +191,7 @@ LLPanelWearableOutfitItem::LLPanelWearableOutfitItem(LLViewerInventoryItem* item
// virtual
void LLPanelWearableOutfitItem::updateItem(const std::string& name,
+ bool favorite,
EItemState item_state)
{
std::string search_label = name;
@@ -254,7 +255,7 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name,
}
}
- LLPanelInventoryListItemBase::updateItem(search_label, item_state);
+ LLPanelInventoryListItemBase::updateItem(search_label, favorite, item_state);
}
//////////////////////////////////////////////////////////////////////////
@@ -481,6 +482,7 @@ LLPanelAttachmentListItem* LLPanelAttachmentListItem::create(LLViewerInventoryIt
}
void LLPanelAttachmentListItem::updateItem(const std::string& name,
+ bool favorite,
EItemState item_state)
{
std::string title_joint = name;
@@ -498,7 +500,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);
}
// Show per-item complexity in COF
@@ -621,7 +623,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);
@@ -1147,7 +1149,6 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()
registrar.add("Wearable.TakeOffDetach",
//boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); // [SL:KB] - Patch: Appearance-Misc
boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids));
-
// Register handlers for clothing.
registrar.add("Clothing.TakeOff",
//boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); // [SL:KB] - Patch: Appearance-Misc
@@ -1159,6 +1160,7 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()
registrar.add("Attachment.Detach",
//boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids, no_op)); // [SL:KB] - Patch: Appearance-Misc
boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids));
+ 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));
@@ -1192,6 +1194,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;
// [RLVa:KB] - Checked: 2010-09-04 (RLVa-1.2.1a) | Added: RLVa-1.2.1a
// We'll enable a menu option if at least one item in the selection is wearable/removable
@@ -1220,6 +1224,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;
@@ -1243,7 +1253,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);
}
// [RLVa:KB] - Checked: 2010-09-04 (RLVa-1.2.1a) | Added: RLVa-1.2.1a
@@ -1308,6 +1318,8 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu
setMenuItemVisible(menu, "delete_from_outfit", n_links > 0);
setMenuItemEnabled(menu, "delete_from_outfit", n_links > 0);
//
+ 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 22be67b1d1..57cb51e01f 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/particleeditor.cpp b/indra/newview/particleeditor.cpp
index 5f8e591dc7..70fab5970d 100644
--- a/indra/newview/particleeditor.cpp
+++ b/indra/newview/particleeditor.cpp
@@ -22,8 +22,6 @@
#include "llviewerprecompiledheaders.h"
#include "particleeditor.h"
-#include
-
#include "llagent.h"
#include "llappviewer.h"
#include "llcheckboxctrl.h"
@@ -47,10 +45,7 @@
#include "llwindow.h"
#include "llviewerassetupload.h"
-ParticleEditor::ParticleEditor(const LLSD& key)
-: LLFloater(key),
- mObject(0),
- mParticleScriptInventoryItem(0)
+ParticleEditor::ParticleEditor(const LLSD& key) : LLFloater(key)
{
mPatternMap["drop"] = LLPartSysData::LL_PART_SRC_PATTERN_DROP;
mPatternMap["explode"] = LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE;
@@ -325,11 +320,11 @@ void ParticleEditor::updateUI()
bool targetLinear = mTargetLinearCheckBox->getValue();
bool interpolateColor = mInterpolateColorCheckBox->getValue();
bool interpolateScale = mInterpolateScaleCheckBox->getValue();
- bool targetEnabled = targetLinear | (mTargetPositionCheckBox->getValue().asBoolean() ? true : false);
+ bool targetEnabled = targetLinear || (mTargetPositionCheckBox->getValue().asBoolean() ? true : false);
- mBurstRadiusSpinner->setEnabled(!(targetLinear | (mFollowSourceCheckBox->getValue().asBoolean() ? true : false) | dropPattern));
- mBurstSpeedMinSpinner->setEnabled(!(targetLinear | dropPattern));
- mBurstSpeedMaxSpinner->setEnabled(!(targetLinear | dropPattern));
+ mBurstRadiusSpinner->setEnabled(!targetLinear && !(mFollowSourceCheckBox->getValue().asBoolean() ? true : false) && !dropPattern);
+ mBurstSpeedMinSpinner->setEnabled(!targetLinear && !dropPattern);
+ mBurstSpeedMaxSpinner->setEnabled(!targetLinear && !dropPattern);
// disabling a color swatch does nothing visually, so we also set alpha
LLColor4 endColor = mEndColorSelector->get();
@@ -355,8 +350,8 @@ void ParticleEditor::updateUI()
mOmegaYSpinner->setEnabled(!targetLinear);
mOmegaZSpinner->setEnabled(!targetLinear);
- mAngleBeginSpinner->setEnabled(!(explodePattern | dropPattern));
- mAngleEndSpinner->setEnabled(!(explodePattern | dropPattern));
+ mAngleBeginSpinner->setEnabled(!explodePattern && !dropPattern);
+ mAngleEndSpinner->setEnabled(!explodePattern && !dropPattern);
}
void ParticleEditor::onClearTargetButtonClicked()
@@ -369,15 +364,13 @@ void ParticleEditor::onTargetPickerButtonClicked()
{
mPickTargetButton->setToggleState(true);
mPickTargetButton->setEnabled(false);
- startPicking(this);
+ startPicking();
}
// inspired by the LLFloaterReporter object picker
-// static
-void ParticleEditor::startPicking(void* userdata)
+void ParticleEditor::startPicking()
{
- ParticleEditor* self = (ParticleEditor*) userdata;
- LLToolObjPicker::getInstance()->setExitCallback(ParticleEditor::onTargetPicked, self);
+ LLToolObjPicker::getInstance()->setExitCallback(ParticleEditor::onTargetPicked, this);
LLToolMgr::getInstance()->setTransientTool(LLToolObjPicker::getInstance());
}
@@ -386,14 +379,12 @@ void ParticleEditor::onTargetPicked(void* userdata)
{
ParticleEditor* self = (ParticleEditor*)userdata;
- LLUUID picked = LLToolObjPicker::getInstance()->getObjectID();
-
LLToolMgr::getInstance()->clearTransientTool();
self->mPickTargetButton->setEnabled(true);
self->mPickTargetButton->setToggleState(false);
- if (picked.notNull())
+ if (LLUUID picked = LLToolObjPicker::getInstance()->getObjectID(); picked.notNull())
{
self->mTargetKeyInput->setValue(picked.asString());
self->onParameterChange();
@@ -522,8 +513,7 @@ default\n\
void ParticleEditor::onCopyButtonClicked()
{
- std::string script = createScript();
- if (!script.empty())
+ if (std::string script = createScript(); !script.empty())
{
getWindow()->copyTextToClipboard(utf8str_to_wstring(script));
LLNotificationsUtil::add("ParticleScriptCopiedToClipboard");
@@ -608,9 +598,7 @@ void ParticleEditor::callbackReturned(const LLUUID& inventoryItemID)
gInventory.notifyObservers();
//caps import
- std::string url = gAgent.getRegionCapability("UpdateScriptAgent");
-
- if (!url.empty())
+ if (std::string url = gAgent.getRegionCapability("UpdateScriptAgent"); !url.empty())
{
std::string script = createScript();
@@ -633,30 +621,23 @@ void ParticleEditor::callbackReturned(const LLUUID& inventoryItemID)
void ParticleEditor::scriptInjectReturned()
{
setCanClose(true);
+ mMainPanel->setEnabled(true);
// play it safe, because some time may have passed
- LLViewerObject* object = gObjectList.findObject(mObject->getID());
- if (!object || mObject->isDead())
+ if (LLViewerObject* object = gObjectList.findObject(mObject->getID()); object && mObject&& !mObject->isDead())
+ {
+ mObject->saveScript(mParticleScriptInventoryItem, true, false);
+ LLNotificationsUtil::add("ParticleScriptInjected");
+ }
+ else
{
LL_WARNS() << "Can't inject script - object is dead or went away!" << LL_ENDL;
- mMainPanel->setEnabled(true);
- return;
}
-
- mObject->saveScript(mParticleScriptInventoryItem, true, false);
- LLNotificationsUtil::add("ParticleScriptInjected");
-
- delete this;
}
// ---------------------------------- Callbacks ----------------------------------
-ParticleScriptCreationCallback::ParticleScriptCreationCallback(ParticleEditor* editor)
-{
- mEditor = editor;
-}
-
-ParticleScriptCreationCallback::~ParticleScriptCreationCallback()
+ParticleScriptCreationCallback::ParticleScriptCreationCallback(ParticleEditor* editor) : mEditor(editor)
{
}
diff --git a/indra/newview/particleeditor.h b/indra/newview/particleeditor.h
index d0865d8644..c930866822 100644
--- a/indra/newview/particleeditor.h
+++ b/indra/newview/particleeditor.h
@@ -43,117 +43,117 @@ class ParticleEditor : public LLFloater
friend class ParticleScriptCreationCallback;
friend class ParticleScriptUploadResponder;
- public:
- ParticleEditor(const LLSD& key);
- ~ParticleEditor();
+public:
+ ParticleEditor(const LLSD& key);
+ ~ParticleEditor();
- bool postBuild() override;
+ bool postBuild() override;
- void setObject(LLViewerObject* objectp);
- void scriptInjectReturned( );
+ void setObject(LLViewerObject* objectp);
+ void scriptInjectReturned();
- protected:
- void clearParticles();
- void updateParticles();
- void updateUI();
+protected:
+ void clearParticles();
+ void updateParticles();
+ void updateUI();
- std::string createScript();
- void createScriptInventoryItem(LLUUID categoryID);
+ std::string createScript();
+ void createScriptInventoryItem(LLUUID categoryID);
- void onParameterChange();
- void onCopyButtonClicked();
- void onInjectButtonClicked();
+ void onParameterChange();
+ void onCopyButtonClicked();
+ void onInjectButtonClicked();
- void onClearTargetButtonClicked();
- void onTargetPickerButtonClicked();
- static void startPicking(void* userdata);
- static void onTargetPicked(void* userdata);
+ void onClearTargetButtonClicked();
+ void onTargetPickerButtonClicked();
+ void startPicking();
+ static void onTargetPicked(void* userdata);
- void callbackReturned(const LLUUID& inv_item);
+ void callbackReturned(const LLUUID& inv_item);
- std::string lslVector(F32 x, F32 y, F32 z);
- std::string lslColor(const LLColor4& color);
+ std::string lslVector(F32 x, F32 y, F32 z);
+ std::string lslColor(const LLColor4& color);
- LLViewerObject* mObject;
- LLViewerTexture* mTexture;
- LLViewerInventoryItem* mParticleScriptInventoryItem;
+ LLViewerObject* mObject{ nullptr };
+ LLViewerTexture* mTexture{ nullptr };
+ LLViewerInventoryItem* mParticleScriptInventoryItem{ nullptr };
- LLViewerTexture* mDefaultParticleTexture;
+ LLViewerTexture* mDefaultParticleTexture{ nullptr };
- LLPartSysData mParticles;
+ LLPartSysData mParticles;
- std::map mPatternMap;
- std::map mScriptPatternMap;
+ std::map mPatternMap;
+ std::map mScriptPatternMap;
- std::map mBlendMap;
- std::map mScriptBlendMap;
+ std::map mBlendMap;
+ std::map mScriptBlendMap;
- LLPanel* mMainPanel;
+ LLPanel* mMainPanel{ nullptr };
- LLComboBox* mPatternTypeCombo;
- LLTextureCtrl* mTexturePicker;
+ LLComboBox* mPatternTypeCombo{ nullptr };
+ LLTextureCtrl* mTexturePicker{ nullptr };
- LLSpinCtrl* mBurstRateSpinner;
- LLSpinCtrl* mBurstCountSpinner;
- LLSpinCtrl* mBurstRadiusSpinner;
- LLSpinCtrl* mAngleBeginSpinner;
- LLSpinCtrl* mAngleEndSpinner;
- LLSpinCtrl* mBurstSpeedMinSpinner;
- LLSpinCtrl* mBurstSpeedMaxSpinner;
- LLSpinCtrl* mStartAlphaSpinner;
- LLSpinCtrl* mEndAlphaSpinner;
- LLSpinCtrl* mScaleStartXSpinner;
- LLSpinCtrl* mScaleStartYSpinner;
- LLSpinCtrl* mScaleEndXSpinner;
- LLSpinCtrl* mScaleEndYSpinner;
- LLSpinCtrl* mSourceMaxAgeSpinner;
- LLSpinCtrl* mParticlesMaxAgeSpinner;
- LLSpinCtrl* mStartGlowSpinner;
- LLSpinCtrl* mEndGlowSpinner;
+ LLSpinCtrl* mBurstRateSpinner{ nullptr };
+ LLSpinCtrl* mBurstCountSpinner{ nullptr };
+ LLSpinCtrl* mBurstRadiusSpinner{ nullptr };
+ LLSpinCtrl* mAngleBeginSpinner{ nullptr };
+ LLSpinCtrl* mAngleEndSpinner{ nullptr };
+ LLSpinCtrl* mBurstSpeedMinSpinner{ nullptr };
+ LLSpinCtrl* mBurstSpeedMaxSpinner{ nullptr };
+ LLSpinCtrl* mStartAlphaSpinner{ nullptr };
+ LLSpinCtrl* mEndAlphaSpinner{ nullptr };
+ LLSpinCtrl* mScaleStartXSpinner{ nullptr };
+ LLSpinCtrl* mScaleStartYSpinner{ nullptr };
+ LLSpinCtrl* mScaleEndXSpinner{ nullptr };
+ LLSpinCtrl* mScaleEndYSpinner{ nullptr };
+ LLSpinCtrl* mSourceMaxAgeSpinner{ nullptr };
+ LLSpinCtrl* mParticlesMaxAgeSpinner{ nullptr };
+ LLSpinCtrl* mStartGlowSpinner{ nullptr };
+ LLSpinCtrl* mEndGlowSpinner{ nullptr };
- LLComboBox* mBlendFuncSrcCombo;
- LLComboBox* mBlendFuncDestCombo;
+ LLComboBox* mBlendFuncSrcCombo{ nullptr };
+ LLComboBox* mBlendFuncDestCombo{ nullptr };
- LLCheckBoxCtrl* mBounceCheckBox;
- LLCheckBoxCtrl* mEmissiveCheckBox;
- LLCheckBoxCtrl* mFollowSourceCheckBox;
- LLCheckBoxCtrl* mFollowVelocityCheckBox;
- LLCheckBoxCtrl* mInterpolateColorCheckBox;
- LLCheckBoxCtrl* mInterpolateScaleCheckBox;
- LLCheckBoxCtrl* mTargetPositionCheckBox;
- LLCheckBoxCtrl* mTargetLinearCheckBox;
- LLCheckBoxCtrl* mWindCheckBox;
- LLCheckBoxCtrl* mRibbonCheckBox;
+ LLCheckBoxCtrl* mBounceCheckBox{ nullptr };
+ LLCheckBoxCtrl* mEmissiveCheckBox{ nullptr };
+ LLCheckBoxCtrl* mFollowSourceCheckBox{ nullptr };
+ LLCheckBoxCtrl* mFollowVelocityCheckBox{ nullptr };
+ LLCheckBoxCtrl* mInterpolateColorCheckBox{ nullptr };
+ LLCheckBoxCtrl* mInterpolateScaleCheckBox{ nullptr };
+ LLCheckBoxCtrl* mTargetPositionCheckBox{ nullptr };
+ LLCheckBoxCtrl* mTargetLinearCheckBox{ nullptr };
+ LLCheckBoxCtrl* mWindCheckBox{ nullptr };
+ LLCheckBoxCtrl* mRibbonCheckBox{ nullptr };
- LLLineEditor* mTargetKeyInput;
- LLButton* mClearTargetButton;
- LLButton* mPickTargetButton;
+ LLLineEditor* mTargetKeyInput{ nullptr };
+ LLButton* mClearTargetButton{ nullptr };
+ LLButton* mPickTargetButton{ nullptr };
- LLSpinCtrl* mAcellerationXSpinner;
- LLSpinCtrl* mAcellerationYSpinner;
- LLSpinCtrl* mAcellerationZSpinner;
+ LLSpinCtrl* mAcellerationXSpinner{ nullptr };
+ LLSpinCtrl* mAcellerationYSpinner{ nullptr };
+ LLSpinCtrl* mAcellerationZSpinner{ nullptr };
- LLSpinCtrl* mOmegaXSpinner;
- LLSpinCtrl* mOmegaYSpinner;
- LLSpinCtrl* mOmegaZSpinner;
+ LLSpinCtrl* mOmegaXSpinner{ nullptr };
+ LLSpinCtrl* mOmegaYSpinner{ nullptr };
+ LLSpinCtrl* mOmegaZSpinner{ nullptr };
- LLColorSwatchCtrl* mStartColorSelector;
- LLColorSwatchCtrl* mEndColorSelector;
+ LLColorSwatchCtrl* mStartColorSelector{ nullptr };
+ LLColorSwatchCtrl* mEndColorSelector{ nullptr };
- LLButton* mCopyToLSLButton;
- LLButton* mInjectScriptButton;
+ LLButton* mCopyToLSLButton{ nullptr };
+ LLButton* mInjectScriptButton{ nullptr };
};
class ParticleScriptCreationCallback : public LLInventoryCallback
{
- public:
- ParticleScriptCreationCallback(ParticleEditor* editor);
- void fire(const LLUUID& inventoryItem);
+public:
+ ParticleScriptCreationCallback(ParticleEditor* editor);
+ void fire(const LLUUID& inventoryItem);
- protected:
- ~ParticleScriptCreationCallback();
+protected:
+ ~ParticleScriptCreationCallback() = default;
- ParticleEditor* mEditor;
+ ParticleEditor* mEditor;
};
#endif // PARTICLEEDITOR_H
diff --git a/indra/newview/res/ll_icon_small.ico b/indra/newview/res/ll_icon_small.ico
new file mode 100644
index 0000000000..a3f6877935
Binary files /dev/null and b/indra/newview/res/ll_icon_small.ico differ
diff --git a/indra/newview/res/resource.h b/indra/newview/res/resource.h
index e904f4a1a8..1d3289d784 100644
--- a/indra/newview/res/resource.h
+++ b/indra/newview/res/resource.h
@@ -30,6 +30,7 @@
#define IDREMOVE 3
#define IDI_LL_ICON 103
#define IDC_GRABHAND 104
+#define IDI_LL_ICON_SMALL 105
#define IDC_CURSOR1 134
#define IDC_CURSOR2 136
#define IDC_CURSOR3 147
diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc
index 9ce3cbe273..9827435a04 100755
--- a/indra/newview/res/viewerRes.rc
+++ b/indra/newview/res/viewerRes.rc
@@ -56,6 +56,7 @@ END
// remains consistent on all systems.
IDI_LL_ICON ICON "firestorm_icon.ico"
IDI_LCD_LL_ICON ICON "firestorm_icon.ico"
+IDI_LL_ICON_SMALL ICON "firestorm_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
diff --git a/indra/newview/skins/ansastorm/xui/de/panel_main_inventory.xml b/indra/newview/skins/ansastorm/xui/de/panel_main_inventory.xml
index 49d13e2182..32c47ed214 100644
--- a/indra/newview/skins/ansastorm/xui/de/panel_main_inventory.xml
+++ b/indra/newview/skins/ansastorm/xui/de/panel_main_inventory.xml
@@ -124,6 +124,7 @@
+
diff --git a/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml b/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml
index 9cc5648c06..07122d6243 100644
--- a/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml
+++ b/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml
@@ -847,6 +847,19 @@
scroll.reserve_scroll_corner="false">
+
+
+
-
+ width="305"/>
Complexity: [WEIGHT]
-
diff --git a/indra/newview/skins/ansastorm/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/ansastorm/xui/en/panel_outfits_wearing.xml
index c32ae40a0a..de570297e8 100644
--- a/indra/newview/skins/ansastorm/xui/en/panel_outfits_wearing.xml
+++ b/indra/newview/skins/ansastorm/xui/en/panel_outfits_wearing.xml
@@ -71,43 +71,21 @@
name="bottom_panel"
top_pad="0"
width="312">
-
-
+ width="305" />
font="SansSerifSmall"
text_color="EmphasisColor"
width="300"
- height="10"
+ height="13"
follows="top|left|right"
layout="topleft"
left="35"
@@ -94,7 +94,7 @@ width="415">
image_overlay="Edit_Wrench"
label=""
layout="topleft"
- left="360"
+ left="364"
name="edit_outfit_btn"
tool_tip="Edit this outfit"
top="3"
@@ -103,23 +103,105 @@ width="415">
follows="top|right"
height="24"
layout="topleft"
- left="363"
+ left="364"
name="wearables_loading_indicator"
top="6"
width="24" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
visible="false"
left="0"
tab_group="1"
- top_pad="6"
+ top_pad="4"
follows="all" />
diff --git a/indra/newview/skins/default/xui/de/floater_fs_poser.xml b/indra/newview/skins/default/xui/de/floater_fs_poser.xml
index 5457822b71..4ffe3de5fc 100644
--- a/indra/newview/skins/default/xui/de/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/de/floater_fs_poser.xml
@@ -199,6 +199,7 @@
Hinein/Heraus:
+
@@ -253,11 +254,11 @@
-
-
+
+
@@ -302,6 +303,7 @@
+
diff --git a/indra/newview/skins/default/xui/de/floater_inventory_settings.xml b/indra/newview/skins/default/xui/de/floater_inventory_settings.xml
index 8637ef7a53..0f5b3aa6d7 100644
--- a/indra/newview/skins/default/xui/de/floater_inventory_settings.xml
+++ b/indra/newview/skins/default/xui/de/floater_inventory_settings.xml
@@ -22,5 +22,17 @@
-
+
+ Favoriten
+
+
+
+
+
+ Drücken von Eingabetaste bei einem Avatar-Anhang
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/floater_object_weights.xml b/indra/newview/skins/default/xui/de/floater_object_weights.xml
index 4bceb534e2..a8c6bb98ee 100644
--- a/indra/newview/skins/default/xui/de/floater_object_weights.xml
+++ b/indra/newview/skins/default/xui/de/floater_object_weights.xml
@@ -1,12 +1,17 @@
-
+
+
+
+
+
+
-
+
@@ -15,7 +20,7 @@
-
+
@@ -24,5 +29,8 @@
-
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/de/menu_gallery_inventory.xml
index 2917e2e07a..2b22624a51 100644
--- a/indra/newview/skins/default/xui/de/menu_gallery_inventory.xml
+++ b/indra/newview/skins/default/xui/de/menu_gallery_inventory.xml
@@ -82,11 +82,22 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_gallery_outfit_tab.xml b/indra/newview/skins/default/xui/de/menu_gallery_outfit_tab.xml
index 776dfde966..cd03000396 100644
--- a/indra/newview/skins/default/xui/de/menu_gallery_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/de/menu_gallery_outfit_tab.xml
@@ -4,6 +4,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_inventory.xml b/indra/newview/skins/default/xui/de/menu_inventory.xml
index dc72a6bbe4..ad43f5c8fd 100644
--- a/indra/newview/skins/default/xui/de/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/de/menu_inventory.xml
@@ -107,6 +107,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/de/menu_inventory_gear_default.xml
index f6af300a31..c930231ec7 100644
--- a/indra/newview/skins/default/xui/de/menu_inventory_gear_default.xml
+++ b/indra/newview/skins/default/xui/de/menu_inventory_gear_default.xml
@@ -44,4 +44,5 @@
+
diff --git a/indra/newview/skins/default/xui/de/menu_outfit_gallery_sort.xml b/indra/newview/skins/default/xui/de/menu_outfit_gallery_sort.xml
new file mode 100644
index 0000000000..7dfe43dcd2
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/menu_outfit_gallery_sort.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_outfit_gear.xml b/indra/newview/skins/default/xui/de/menu_outfit_gear.xml
index 8412530d03..79344f757b 100644
--- a/indra/newview/skins/default/xui/de/menu_outfit_gear.xml
+++ b/indra/newview/skins/default/xui/de/menu_outfit_gear.xml
@@ -4,6 +4,8 @@
+
+
@@ -31,4 +33,5 @@
+
diff --git a/indra/newview/skins/default/xui/de/menu_outfit_list_sort.xml b/indra/newview/skins/default/xui/de/menu_outfit_list_sort.xml
new file mode 100644
index 0000000000..2e8381985a
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/menu_outfit_list_sort.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_outfit_tab.xml b/indra/newview/skins/default/xui/de/menu_outfit_tab.xml
index 2594dd335e..fc9480a6b8 100644
--- a/indra/newview/skins/default/xui/de/menu_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/de/menu_outfit_tab.xml
@@ -4,6 +4,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml
index 1c5574bebf..1ec6af7567 100644
--- a/indra/newview/skins/default/xui/de/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/de/menu_viewer.xml
@@ -248,6 +248,13 @@
+
diff --git a/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml
index 73dfa21705..2665120544 100644
--- a/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml
+++ b/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml
@@ -11,6 +11,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_wearing_tab.xml b/indra/newview/skins/default/xui/de/menu_wearing_tab.xml
index 93c8020551..e85eb87694 100644
--- a/indra/newview/skins/default/xui/de/menu_wearing_tab.xml
+++ b/indra/newview/skins/default/xui/de/menu_wearing_tab.xml
@@ -6,5 +6,7 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml
index 3121ea62d7..e448e8b932 100644
--- a/indra/newview/skins/default/xui/de/notifications.xml
+++ b/indra/newview/skins/default/xui/de/notifications.xml
@@ -518,6 +518,9 @@ Um Medien nur auf einer Fläche einzufügen, wählen Sie „Oberfläche auswähl
Es kostet L$[COST], um diesen Gegenstand hochzuladen.
+
+ Textur konnte aus folgendem Grund nicht kodiert werden: [REASON]
+
Es kostet L$[COST], um eine Textur in Ihrem Inventar zu speichern. Sie können entweder L$ kaufen oder das Foto auf Ihrem Computer speichern.
@@ -1294,6 +1297,15 @@ Sie sind nicht berechtigt, Land für die aktive Gruppe zu kaufen.
+
+ „[LIST_NAME]“ enthält [MAP_SIZE] Einträge.
+
+Sind Sie sicher, dass Sie diese Liste löschen möchten?
+
+
Das Schlüsselwort muss ein einziges Wort sein; die Ersetzungszeichenfolge darf nicht leer sein.
@@ -5144,6 +5156,10 @@ Möchten Sie fortfahren?
Material kann nicht auf Wasser-Ausschluss-Oberfläche angewandt werden.
+
+ Die von Ihnen hochgeladene Textur wurde von [ORIGINAL_WIDTH]x[ORIGINAL_HEIGHT] Pixel zu [NEW_WIDTH]x[NEW_HEIGHT] Pixel verkleinert, um innerhalb des Maximums von [MAX_WIDTH]x[MAX_HEIGHT] Pixel zu sein.
+
+
Keine gültige Einstellung für die Umgebung ausgewählt.
diff --git a/indra/newview/skins/default/xui/de/panel_login_first.xml b/indra/newview/skins/default/xui/de/panel_login_first.xml
index 034c2d60cc..038001157e 100644
--- a/indra/newview/skins/default/xui/de/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/de/panel_login_first.xml
@@ -4,7 +4,7 @@
http://secondlife.com/account/request.php?lang=de
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
diff --git a/indra/newview/skins/default/xui/de/panel_main_inventory.xml b/indra/newview/skins/default/xui/de/panel_main_inventory.xml
index 0a1e06eefa..d4c7d7082d 100644
--- a/indra/newview/skins/default/xui/de/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/de/panel_main_inventory.xml
@@ -55,7 +55,7 @@
-
+
diff --git a/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
index e8aa7b83ba..f044aa769f 100644
--- a/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
+++ b/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
@@ -26,6 +26,7 @@
+
diff --git a/indra/newview/skins/default/xui/de/sidepanel_appearance.xml b/indra/newview/skins/default/xui/de/sidepanel_appearance.xml
index 7fb9b34e0a..37b45e1c56 100644
--- a/indra/newview/skins/default/xui/de/sidepanel_appearance.xml
+++ b/indra/newview/skins/default/xui/de/sidepanel_appearance.xml
@@ -15,5 +15,18 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index d2ee4faed1..949726a1ea 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -1431,6 +1431,9 @@ https://www.firestormviewer.org/support für Hilfe zum Lösen des Problems.
COLLADA-Modelle
+
+ Mesh-Modelle
+
Komma-separierte Werte
@@ -5383,8 +5386,11 @@ Missbrauchsbericht
Keine/Keiner
+
+ Bilder, die mehr als [PIXELS] Pixel haben, können nicht geladen werden.
+
- Bilder, die größer sind als [WIDTH]*[HEIGHT] können nicht geladen werden
+ Bilder, die größer sind als [WIDTH]*[HEIGHT] Pixel, können nicht geladen werden.
Inkorrektes Bildformat.
diff --git a/indra/newview/skins/default/xui/en/floater_fs_poser.xml b/indra/newview/skins/default/xui/en/floater_fs_poser.xml
index dc6646c554..a2d35e8384 100644
--- a/indra/newview/skins/default/xui/en/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/en/floater_fs_poser.xml
@@ -11,6 +11,11 @@ width="430">
Inv_BodyShape
Inv_Object
+ Script_Running
+ Script_NotRunning
+ Script_Error
+ Locked_Icon
+ Conv_toolbar_close
Check_Mark
Icon_Dock_Foreground
Parcel_Exp_Color
@@ -548,6 +553,17 @@ width="430">
function="Poser.CommitSpinner"
parameter="2"/>
+
tool_tip="Not stopping your pose can be helpful if you do a lot of work, and don't want to accidentally lose it."
top_pad="10"
width="134" />
-
left="5"
tool_tip="The skeleton has unnatural joint rotations by default. This complicates posing. When checked, the joints will rotate in a more natural way."
top_pad="5"
- width="134" />
+ width="134" />
left="5"
tool_tip="Show small indicators to aid joint selection when visually posing."
top_pad="5"
- width="134" />
+ width="134" />
+
+
@@ -1594,7 +1623,7 @@ width="430">
tool_tip="Copy changes to the opposite joint."
left_pad="1"
top_delta="0"
- width="52" >
+ width="43" >
diff --git a/indra/newview/skins/default/xui/en/floater_inventory_settings.xml b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml
index de1f93b0b7..c14c3e5fc6 100644
--- a/indra/newview/skins/default/xui/en/floater_inventory_settings.xml
+++ b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml
@@ -4,8 +4,8 @@
can_minimize="true"
can_resize="false"
save_rect="true"
- height="370"
- width="370"
+ height="483"
+ width="483"
name="inventory_settings"
title="Inventory Settings">
+
Clicking on "Show in inventory" or "Find original"
@@ -146,10 +156,10 @@
font="SansSerifMedium"
left="60"
width="300"
- height="45"
+ height="44"
name="find_original_settings">
-
+ left="18"
+ mouse_opaque="true"
+ name="favorites_icon"
+ top_pad="19"
+ width="18" />
+
+ Favorites
+
+
+
+
+
+
+
+
+
+
+ Pressing enter on an avatar attachment
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml
index 57b74b360a..9981e5d893 100644
--- a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml
+++ b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml
@@ -17,17 +17,7 @@
New inventory features
-You can now add preview images to inventory items and view a folder in its own window.
-Learn more in this [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ blogpost]
-
-
-New GLTF PBR materials support
-
-
-You can now use expanded material support with the ability to import and edit GLTF Physically Based Rendering (PBR) Materials.
-In order to support the addition of the GLTF format, some areas in the viewer may appear darker than usual.
-
-Learn more about [https://wiki.secondlife.com/wiki/PBR_Materials Physically Based Rendering (PBR)]
+You can now mark items and folders as favorites. Favorited items will appear in the Favorites tab of inventory and by default will be highlighted with a star in the main inventory view.
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index af2ed746c7..a446be537a 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -931,7 +931,7 @@
layout="topleft"
left="420"
name="Mirrors"
- top_delta="24"
+ top_delta="22"
width="240">
@@ -1033,7 +1033,7 @@
max_val="1.0"
name="RenderSharpness"
show_text="true"
- top_delta="24"
+ top_delta="20"
width="260">
@@ -1084,7 +1084,7 @@
max_val="1.0"
name="TonemapMix"
show_text="true"
- top_delta="22"
+ top_delta="20"
width="260">
diff --git a/indra/newview/skins/default/xui/en/floater_preview_trash.xml b/indra/newview/skins/default/xui/en/floater_preview_trash.xml
index abf8227ca0..bb0d559fc0 100644
--- a/indra/newview/skins/default/xui/en/floater_preview_trash.xml
+++ b/indra/newview/skins/default/xui/en/floater_preview_trash.xml
@@ -27,6 +27,8 @@
bevel_style="none"
scroll.reserve_scroll_corner="false">
+
+
- secondlife://app/remove_folder/?folder_id=
+ secondlife:///app/remove_folder/?folder_id=
- secondlife://app/wear_folder/?folder_name=Daisy
+ secondlife:///app/wear_folder/?folder_name=Daisy
- secondlife://app/add_folder/?folder_name=Cardboard%20Boxbot
+ secondlife:///app/add_folder/?folder_name=Cardboard%20Boxbot
- secondlife://app/add_folder/?folder_id=59219db2-c260-87d3-213d-bb3bc298a3d8
+ secondlife:///app/add_folder/?folder_id=59219db2-c260-87d3-213d-bb3bc298a3d8
- secondlife://app/remove_folder/?folder_id=
+ secondlife:///app/remove_folder/?folder_id=
diff --git a/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml
index debb685752..246fac181c 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml
@@ -22,7 +22,7 @@
increment="0.025"
initial_value="0.5"
label="Voice Chat"
- label_width="50"
+ label_width="60"
layout="topleft"
left="15"
top="50"
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
index d9f847a4fa..1b5ba90eec 100644
--- a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
@@ -670,6 +670,99 @@
function="Inventory.DoToSelected"
parameter="ungroup_folder_items" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gallery_sort.xml b/indra/newview/skins/default/xui/en/menu_outfit_gallery_sort.xml
new file mode 100644
index 0000000000..aa4cd1483d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_outfit_gallery_sort.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
index d121154219..4635ac0db3 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml
@@ -49,6 +49,26 @@
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -277,4 +269,19 @@
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_list_sort.xml b/indra/newview/skins/default/xui/en/menu_outfit_list_sort.xml
new file mode 100644
index 0000000000..0a4d1ea877
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_outfit_list_sort.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
index 7aa49e78f6..8dd508fd1c 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
@@ -49,6 +49,26 @@
+
+
+
+
+
+
+
+
+
+ function="Url.ShowParcelOnMap" />
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 6e38c08951..103e7fc5b2 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2226,6 +2226,64 @@
parameter="RenderDisablePostProcessing" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index ec2b0c2b3f..d21531516b 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -1320,6 +1320,14 @@ Error encoding snapshot.
fail
+
+ Failed to encode image, reason: [REASON]
+ fail
+
+
@@ -3304,6 +3312,29 @@ Would you be my friend?
+'[LIST_NAME]' contains [MAP_SIZE] entries.
+
+Are you sure you want to delete this list?
+ confirm
+
+
+
+
The keyword must be a single word, and the replacement may not be empty.
@@ -4763,13 +4794,12 @@ You already have blocked/muted this name.
-Though permitted, deleting contents may damage the object. Do you want to delete that item?
+You don't have permission to modify content of this object
confirm
@@ -14584,6 +14614,17 @@ This will replace the items in the selected outfit with the items you are wearin
fail
+
+ The texture you are uploading has been resized from [ORIGINAL_WIDTH]x[ORIGINAL_HEIGHT] to [NEW_WIDTH]x[NEW_HEIGHT] in order to to fit the maximum size of [MAX_WIDTH]x[MAX_HEIGHT] pixels.
+
+
+
+
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
+
+
+
-
+
diff --git a/indra/newview/skins/default/xui/en/panel_marketplace_listings_listed.xml b/indra/newview/skins/default/xui/en/panel_marketplace_listings_listed.xml
index fde8d6aa10..38d8bf194d 100644
--- a/indra/newview/skins/default/xui/en/panel_marketplace_listings_listed.xml
+++ b/indra/newview/skins/default/xui/en/panel_marketplace_listings_listed.xml
@@ -20,5 +20,5 @@
border="false"
bevel_style="none"
show_item_link_overlays="true">
-
+
diff --git a/indra/newview/skins/default/xui/en/panel_marketplace_listings_unassociated.xml b/indra/newview/skins/default/xui/en/panel_marketplace_listings_unassociated.xml
index 8d17012117..6a3fef2b03 100644
--- a/indra/newview/skins/default/xui/en/panel_marketplace_listings_unassociated.xml
+++ b/indra/newview/skins/default/xui/en/panel_marketplace_listings_unassociated.xml
@@ -19,5 +19,5 @@
border="false"
bevel_style="none"
show_item_link_overlays="true">
-
+
diff --git a/indra/newview/skins/default/xui/en/panel_marketplace_listings_unlisted.xml b/indra/newview/skins/default/xui/en/panel_marketplace_listings_unlisted.xml
index 70f0302827..641bb1574a 100644
--- a/indra/newview/skins/default/xui/en/panel_marketplace_listings_unlisted.xml
+++ b/indra/newview/skins/default/xui/en/panel_marketplace_listings_unlisted.xml
@@ -20,5 +20,5 @@
border="false"
bevel_style="none"
show_item_link_overlays="true">
-
+
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
index f101e334b5..cdbec967ba 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml
@@ -54,27 +54,14 @@
visible="true"
name="bottom_panel"
width="312">
-
+ width="305"/>
Complexity: [WEIGHT]
-
diff --git a/indra/newview/skins/default/xui/en/panel_outfits_list.xml b/indra/newview/skins/default/xui/en/panel_outfits_list.xml
index 3004ae322e..a038888b25 100644
--- a/indra/newview/skins/default/xui/en/panel_outfits_list.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfits_list.xml
@@ -39,27 +39,14 @@
visible="true"
name="bottom_panel"
width="312">
-
+ width="305"/>
Complexity: [WEIGHT]
-
diff --git a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
index ad3a99f385..d2be760b01 100644
--- a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
@@ -75,43 +75,21 @@
name="bottom_panel"
top_pad="0"
width="312">
-
-
+ width="305" />
+
font="SansSerifSmall"
text_color="EmphasisColor"
width="300"
- height="10"
+ height="13"
follows="top|left|right"
layout="topleft"
left="35"
@@ -94,7 +94,7 @@ width="333">
image_overlay="Edit_Wrench"
label=""
layout="topleft"
- left="265"
+ right="-3"
name="edit_outfit_btn"
tool_tip="Edit this outfit"
top="3"
@@ -108,18 +108,100 @@ width="333">
top="6"
width="24" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
visible="false"
left="0"
tab_group="1"
- top_pad="6"
+ top_pad="4"
follows="all" />
Dictionaries
Object Backups
COLLADA Models
+ Mesh Models
Comma separated values
Recompile
@@ -791,8 +792,9 @@ https://www.firestormviewer.org/support for help fixing this problem.
Didn't find what you're looking for? Try [secondlife:///app/search/all/[SEARCH_TERM] Search].
- Didn't find what you're looking for? Show [secondlife:///app/inventory/filters Filters].
- Didn't find what you're looking for? Try [secondlife:///app/search/places/[SEARCH_TERM] Search].
+ Didn't find what you're looking for? Try [secondlife:///app/inventory/filters Show filters].
+ You haven't marked any items as favorites.
+ To add a place to your landmarks, click the star to the right of the location name.
Drag a landmark here to add it to your favorites.
You have no listings yet.
No items found. Check the spelling of your search string and try again.
@@ -964,11 +966,12 @@ If you continue to receive this message, please contact Second Life support for
Uncompressed Sounds
Animations
Gestures
- Favorites
+ Favorites
Favorites
+ Favorites
Favorites
Current Outfit
Initial Outfits
@@ -2493,6 +2496,7 @@ Abuse Report
[mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]
none/none
+ Can't load images larger than [PIXELS] pixels
Can't load images larger than [WIDTH]*[HEIGHT]
Incorrect image format.
File is empty.
diff --git a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml
index b598bbccd8..50c5285e04 100644
--- a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml
+++ b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml
@@ -1,6 +1,8 @@
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
diff --git a/indra/newview/skins/default/xui/fr/panel_login_first.xml b/indra/newview/skins/default/xui/fr/panel_login_first.xml
index 67998b37ef..8f40d0230c 100644
--- a/indra/newview/skins/default/xui/fr/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/fr/panel_login_first.xml
@@ -4,7 +4,7 @@
http://secondlife.com/account/request.php?lang=fr
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
@@ -13,14 +13,13 @@
-
- S'inscrire
-
-
-
+
Mot de passe oublié
+
+ S'inscrire
+
diff --git a/indra/newview/skins/default/xui/it/panel_login_first.xml b/indra/newview/skins/default/xui/it/panel_login_first.xml
index cb3d7c656e..5b04fd411a 100644
--- a/indra/newview/skins/default/xui/it/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/it/panel_login_first.xml
@@ -4,7 +4,7 @@
http://secondlife.com/account/request.php?lang=it
- https://www.firestormviewer.org/join-secondlife/
+ http://join.secondlife.com/
diff --git a/indra/newview/skins/default/xui/ja/panel_login_first.xml b/indra/newview/skins/default/xui/ja/panel_login_first.xml
index 21e8ed70ca..0f987fc816 100644
--- a/indra/newview/skins/default/xui/ja/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/ja/panel_login_first.xml
@@ -3,23 +3,52 @@
https://secondlife.com/my/account/request.php?lang=ja-JP
+
+ https://join.secondlife.com/?lang=ja
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
+
サインアップ
-
-
+
+
パスワードを忘れましたか?
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/floater_fs_poser.xml b/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
index f33b8e9a7e..062f8a42cc 100644
--- a/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
@@ -194,6 +194,7 @@
Przód/Tył:
+
@@ -245,11 +246,11 @@
Czułość trackpada:
-
-
+
+
@@ -294,6 +295,7 @@
+
diff --git a/indra/newview/skins/default/xui/pl/floater_inventory_settings.xml b/indra/newview/skins/default/xui/pl/floater_inventory_settings.xml
index 7374f30268..449faec7d1 100644
--- a/indra/newview/skins/default/xui/pl/floater_inventory_settings.xml
+++ b/indra/newview/skins/default/xui/pl/floater_inventory_settings.xml
@@ -22,4 +22,17 @@
+
+ Ulubione
+
+
+
+
+
+ Naciśnięcie "Enter" na dodatku awatara
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/pl/floater_new_feature_notification.xml
index 6eaa182d77..47441f2d87 100644
--- a/indra/newview/skins/default/xui/pl/floater_new_feature_notification.xml
+++ b/indra/newview/skins/default/xui/pl/floater_new_feature_notification.xml
@@ -4,17 +4,7 @@
Nowe funkcje szafy
- Możesz teraz dodawać zdjęcia podglądu do elementów szafy i przeglądać foldery w osobnych oknach.
-Dowiedz się więcej w tym [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ poście].
-
-
- Nowe wsparcie dla materiałów GLTF PBR
-
-
- Możesz teraz korzystać z rozszerzonej obsługi materiałów z możliwością importowania i edytowania ich dla renderowania PBR - GLTF.
-Aby umożliwić dodanie formatu GLTF, niektóre obszary mogą się teraz wydawać ciemniejsze niż zwykle.
-
-Więcej na temat: [https://wiki.secondlife.com/wiki/PBR_Materials Physically Based Rendering (PBR)]
+ Możesz teraz oznaczać przedmioty i foldery jako ulubione. Ulubione elementy pojawią się w zakładce Ulubione w szafie i domyślnie będą wyróżnione gwiazdką w głównym widoku.
Nowa funkcja
diff --git a/indra/newview/skins/default/xui/pl/floater_object_weights.xml b/indra/newview/skins/default/xui/pl/floater_object_weights.xml
index d3040906b9..73d1800819 100644
--- a/indra/newview/skins/default/xui/pl/floater_object_weights.xml
+++ b/indra/newview/skins/default/xui/pl/floater_object_weights.xml
@@ -1,5 +1,10 @@
+
+
+
+
+
@@ -13,5 +18,8 @@
-
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/floater_stats.xml b/indra/newview/skins/default/xui/pl/floater_stats.xml
index 6bc983e998..a34094ac47 100644
--- a/indra/newview/skins/default/xui/pl/floater_stats.xml
+++ b/indra/newview/skins/default/xui/pl/floater_stats.xml
@@ -8,6 +8,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/pl/menu_gallery_inventory.xml
index 00753322a5..d3a3a8bcaf 100644
--- a/indra/newview/skins/default/xui/pl/menu_gallery_inventory.xml
+++ b/indra/newview/skins/default/xui/pl/menu_gallery_inventory.xml
@@ -84,11 +84,22 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_gallery_outfit_tab.xml b/indra/newview/skins/default/xui/pl/menu_gallery_outfit_tab.xml
index 125226a48c..09593ce1f9 100644
--- a/indra/newview/skins/default/xui/pl/menu_gallery_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/pl/menu_gallery_outfit_tab.xml
@@ -4,6 +4,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_inventory.xml b/indra/newview/skins/default/xui/pl/menu_inventory.xml
index d61192e304..990eb2808d 100644
--- a/indra/newview/skins/default/xui/pl/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/pl/menu_inventory.xml
@@ -109,6 +109,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_inventory_view_default.xml b/indra/newview/skins/default/xui/pl/menu_inventory_view_default.xml
index 95c6f87dfd..3c04462be4 100644
--- a/indra/newview/skins/default/xui/pl/menu_inventory_view_default.xml
+++ b/indra/newview/skins/default/xui/pl/menu_inventory_view_default.xml
@@ -7,5 +7,4 @@
-
diff --git a/indra/newview/skins/default/xui/pl/menu_outfit_gallery_sort.xml b/indra/newview/skins/default/xui/pl/menu_outfit_gallery_sort.xml
new file mode 100644
index 0000000000..c4261852ce
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/menu_outfit_gallery_sort.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_outfit_gear.xml b/indra/newview/skins/default/xui/pl/menu_outfit_gear.xml
index 63fedeffdb..32bff26949 100644
--- a/indra/newview/skins/default/xui/pl/menu_outfit_gear.xml
+++ b/indra/newview/skins/default/xui/pl/menu_outfit_gear.xml
@@ -4,12 +4,11 @@
+
+
-
-
-
@@ -31,4 +30,5 @@
+
diff --git a/indra/newview/skins/default/xui/pl/menu_outfit_list_sort.xml b/indra/newview/skins/default/xui/pl/menu_outfit_list_sort.xml
new file mode 100644
index 0000000000..317f0c485d
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/menu_outfit_list_sort.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_outfit_tab.xml b/indra/newview/skins/default/xui/pl/menu_outfit_tab.xml
index 408118c59a..911c53b565 100644
--- a/indra/newview/skins/default/xui/pl/menu_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/pl/menu_outfit_tab.xml
@@ -4,6 +4,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_viewer.xml b/indra/newview/skins/default/xui/pl/menu_viewer.xml
index 13f65a23cd..5a5c254e6d 100644
--- a/indra/newview/skins/default/xui/pl/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/pl/menu_viewer.xml
@@ -249,6 +249,13 @@
+
diff --git a/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml
index f7e0525687..43b7681de0 100644
--- a/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml
+++ b/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml
@@ -11,6 +11,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml b/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml
index 33670d0590..c96d970524 100644
--- a/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml
+++ b/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml
@@ -5,6 +5,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml
index 1136f4cf90..358aef673d 100644
--- a/indra/newview/skins/default/xui/pl/notifications.xml
+++ b/indra/newview/skins/default/xui/pl/notifications.xml
@@ -443,6 +443,9 @@ W celu umieszczenia mediów tylko na jednej powierzchni skorzystaj z narzędzia
Potrzebujesz [COST]L$ aby przesłać ten element.
+
+ Nie udało się zakodować obrazu, powód: [REASON]
+
Potrzebujesz [COST]L$ aby zapisać teksturę do swojej Szafy. Możesz zamiast tego kupić L$ lub zapisać teksturę na swoim komputerze.
@@ -1224,6 +1227,15 @@ Wprowadź nową nazwę:
+
+ '[LIST_NAME]' zawiera [MAP_SIZE] wpisów.
+
+Czy na pewno chcesz usunąć tę listę?
+
+
Słowo kluczowe musi być pojedynczym ciągiem, a zamiennik nie może być pusty.
@@ -1806,9 +1818,8 @@ Obecnie ponowne dołączenie do tej grupy nie wiąże się z żadnymi opłatami.
Rezydent/obiekt jest już zablokowany.
-
- Pomimo, że jest to dozwolone, usunięcie zawartości może uszkodzić obiekt. Chcesz usunąć?
-
+
+ Nie masz uprawnień do modyfikowania zawartości tego obiektu
Nie możesz dać wizytówki w tym momencie. Spróbuj jeszcze raz za chwilę.
@@ -5447,6 +5458,10 @@ Czy chcesz kontynuować?
Nie można nałożyć materiału na powierzchnię, która ukrywa wodę.
+
+ Tekstura, którą przesyłasz, została przeskalowana z [ORIGINAL_WIDTH]x[ORIGINAL_HEIGHT] do [NEW_WIDTH]x[NEW_HEIGHT], aby dopasować się do maksymalnego rozmiaru [MAX_WIDTH]x[MAX_HEIGHT] pikseli.
+
+
Zamierzasz włączyć funkcję AutoFPS. Wszystkie niezapisane ustawienia grafiki zostaną utracone.
diff --git a/indra/newview/skins/default/xui/pl/panel_login_first.xml b/indra/newview/skins/default/xui/pl/panel_login_first.xml
index bd8d52fbcf..0604ecbcff 100644
--- a/indra/newview/skins/default/xui/pl/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/pl/panel_login_first.xml
@@ -7,16 +7,21 @@
-
- Dołącz
-
-
-
+
Zapomniałem/am hasła
-
- Pomoc z hasłem
+
+
+
+
+
+
+
+ Wyspa Nauki to Twój pierwszy krok. Znajdź portal z wyjściem!
+
+
+ Potem zwiedź Wyspę Towarzyską i poznaj innych nowych rezydentów!
diff --git a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
index 9b4da2bca8..c7cc9de066 100644
--- a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml
@@ -56,6 +56,7 @@
+
diff --git a/indra/newview/skins/default/xui/pl/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/pl/panel_outfit_gallery.xml
index 3ae12cfa6b..cf326cf113 100644
--- a/indra/newview/skins/default/xui/pl/panel_outfit_gallery.xml
+++ b/indra/newview/skins/default/xui/pl/panel_outfit_gallery.xml
@@ -13,13 +13,11 @@
Szukam...
-
Strojów: [COUNT]
Złożoność: [WEIGHT]
-
diff --git a/indra/newview/skins/default/xui/pl/panel_outfits_list.xml b/indra/newview/skins/default/xui/pl/panel_outfits_list.xml
index 55178339a9..42c392531f 100644
--- a/indra/newview/skins/default/xui/pl/panel_outfits_list.xml
+++ b/indra/newview/skins/default/xui/pl/panel_outfits_list.xml
@@ -5,13 +5,11 @@
-
[COUNT] strojów
Złożoność: [WEIGHT]
-
diff --git a/indra/newview/skins/default/xui/pl/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/pl/panel_outfits_wearing.xml
index 677fe4552f..13afc8e7eb 100644
--- a/indra/newview/skins/default/xui/pl/panel_outfits_wearing.xml
+++ b/indra/newview/skins/default/xui/pl/panel_outfits_wearing.xml
@@ -8,7 +8,6 @@
-
Złożoność: [WEIGHT]
diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/pl/panel_preferences_firestorm.xml
index b2347a68bb..3c94a8f987 100644
--- a/indra/newview/skins/default/xui/pl/panel_preferences_firestorm.xml
+++ b/indra/newview/skins/default/xui/pl/panel_preferences_firestorm.xml
@@ -23,6 +23,7 @@
+
diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_move.xml b/indra/newview/skins/default/xui/pl/panel_preferences_move.xml
index 98fe9120b4..d6f385d581 100644
--- a/indra/newview/skins/default/xui/pl/panel_preferences_move.xml
+++ b/indra/newview/skins/default/xui/pl/panel_preferences_move.xml
@@ -46,7 +46,7 @@
-
+
diff --git a/indra/newview/skins/default/xui/pl/sidepanel_appearance.xml b/indra/newview/skins/default/xui/pl/sidepanel_appearance.xml
index 0f3a18e8e4..e586c9071c 100644
--- a/indra/newview/skins/default/xui/pl/sidepanel_appearance.xml
+++ b/indra/newview/skins/default/xui/pl/sidepanel_appearance.xml
@@ -7,5 +7,18 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index 48e0d90139..8eee1514be 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -1387,6 +1387,9 @@ https://www.firestormviewer.org/support aby uzyskać pomoc.
Modele COLLADA
+
+ Modele meszowe
+
Wartości oddzielone przecinkami
@@ -1675,8 +1678,11 @@ https://www.firestormviewer.org/support aby uzyskać pomoc.
Nie udało Ci się znaleźć tego, czego szukasz? Spróbuj [secondlife:///app/inventory/filters Filtrowania].
+
+ Nie oznaczyłeś/aś jeszcze żadnych elementów jako ulubione.
+
- Nie udało Ci się znaleźć tego, czego szukasz? Spróbuj [secondlife:///app/search/places/[SEARCH_TERM] Wyszukiwarki].
+ Aby dodać miejsce do landmarków, kliknij gwiazdkę po prawej stronie nazwy lokalizacji.
Przeciągnij landmark tutaj aby dodać go do swoich ulubionych.
@@ -1955,12 +1961,15 @@ Jeśli ciągle otrzymujesz tą wiadomość, to skontaktuj się z pomocą technic
Gesty
-
+
Ulubione
Ulubione
+
+ Ulubione
+
Ulubione
@@ -5149,6 +5158,9 @@ Raport o Nadużyciu
brak/brak
+
+ Nie można wczytać obrazów większych niż [PIXELS] pikseli.
+
Nie można załadować obrazów większych niż [WIDTH]*[HEIGHT]
diff --git a/indra/newview/skins/default/xui/pt/panel_login_first.xml b/indra/newview/skins/default/xui/pt/panel_login_first.xml
index 8b2ef04f95..86c61163bc 100644
--- a/indra/newview/skins/default/xui/pt/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/pt/panel_login_first.xml
@@ -4,7 +4,7 @@
http://secondlife.com/account/request.php?lang=pt
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
diff --git a/indra/newview/skins/default/xui/ru/panel_login_first.xml b/indra/newview/skins/default/xui/ru/panel_login_first.xml
index b9fa098867..5db81ea7ca 100644
--- a/indra/newview/skins/default/xui/ru/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/ru/panel_login_first.xml
@@ -1,20 +1,39 @@
+
-
-
-
-
-
-
-
- Регистрация
-
-
- Забыли пароль
-
-
-
-
+
+ http://secondlife.com/account/request.php
+
+
+ https://join.secondlife.com/
+
+
+
+
+
+
+
+
+
+
+ Забытый пароль
+
+
+ Регистрация
+
+
+
+
+
+
+
+
+ Ваш первый шаг – Учебный остров. Найдите портал выхода!
+
+
+ Затем исследуйте Социальный остров и познакомьтесь с другими новичками!
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/tr/panel_login_first.xml b/indra/newview/skins/default/xui/tr/panel_login_first.xml
index 46dc8e8414..1fc80c2b97 100644
--- a/indra/newview/skins/default/xui/tr/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/tr/panel_login_first.xml
@@ -4,7 +4,7 @@
http://secondlife.com/account/request.php
- https://www.firestormviewer.org/join-secondlife/
+ https://join.secondlife.com/
diff --git a/indra/newview/skins/default/xui/zh/panel_login_first.xml b/indra/newview/skins/default/xui/zh/panel_login_first.xml
index 920c64f9a0..4d72fcdd03 100644
--- a/indra/newview/skins/default/xui/zh/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/zh/panel_login_first.xml
@@ -1,20 +1,25 @@
+
+ http://secondlife.com/account/request.php
+
+
+ http://join.secondlife.com/
+
-
-
-
-
- 註冊
-
-
-
+
+
+
+
忘記密碼
+
+ 註冊
+
@@ -31,4 +36,4 @@
-
\ No newline at end of file
+
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
index d5e281bba8..10c68432a1 100644
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -43,12 +43,15 @@ namespace LLStatViewer
LLTrace::SampleStatHandle<> FPS_SAMPLE("fpssample");
}
-void LLVOAvatar::getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars)
+void LLVOAvatar::getNearbyRezzedStats(std::vector& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)
{
counts.resize(3);
counts[0] = 0;
counts[1] = 0;
counts[2] = 1;
+ cloud_avatars = 0;
+ pending_meshes = 0;
+ control_avatars = 0;
}
// static
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 5ae35fddc2..115e910f4b 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -205,8 +205,8 @@ class ViewerManifest(LLManifest,FSViewerManifest):
self.path("skins.xml")
# include the entire textures directory recursively
with self.prefix(src_dst="*/textures"):
+ self.path("*/*.jpg") # Needed for Firestorm skins
self.path("*/*.tga") # Needed for legacy icons
- self.path("*/*.jpg")
self.path("*/*.png")
self.path("*.tga")
self.path("*.j2c")
@@ -675,6 +675,8 @@ class Windows_x86_64_Manifest(ViewerManifest):
):
self.path(libfile)
+ if self.args['discord'] == 'ON':
+ self.path("discord_partner_sdk.dll")
# Mesh 3rd party libs needed for auto LOD and collada reading
try:
@@ -1637,6 +1639,13 @@ class Darwin_x86_64_Manifest(ViewerManifest):
):
self.path2basename(relpkgdir, libfile)
+ # Discord social SDK
+ if self.args['discord'] == 'ON':
+ for libfile in (
+ "libdiscord_partner_sdk.dylib",
+ ):
+ self.path2basename(relpkgdir, libfile)
+
# Fmod studio dylibs (vary based on configuration)
# Fix intolerant processing of booleans
# if self.args['fmodstudio'].lower() == 'ON':
@@ -2489,6 +2498,7 @@ if __name__ == "__main__":
extra_arguments = [
dict(name='bugsplat', description="""BugSplat database to which to post crashes,
if BugSplat crash reporting is desired""", default=''),
+ dict(name='discord', description="""Indication discord social sdk libraries are needed""", default='OFF'),
dict(name='fmodstudio', description="""Indication if fmod studio libraries are needed""", default='OFF'),
dict(name='openal', description="""Indication openal libraries are needed""", default='OFF'),
dict(name='tracy', description="""Indication tracy profiler is enabled""", default='OFF'),