Merge branch 'release/2025.05' of https://github.com/secondlife/viewer

# Conflicts:
#	.github/workflows/qatest.yaml
#	indra/newview/lltexturectrl.cpp
#	indra/newview/llviewerinventory.cpp
#	indra/newview/llviewerobject.cpp
#	indra/newview/skins/default/xui/en/floater_inventory_settings.xml
master
Ansariel 2025-05-23 12:44:50 +02:00
commit 1513d97e55
37 changed files with 692 additions and 114 deletions

View File

@ -1,4 +1,7 @@
name: Run QA Test # Runs automated tests on a self-hosted QA machine
permissions:
contents: read
#pull-requests: write # maybe need to re-add this later
on:
workflow_run:
@ -15,11 +18,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Debug Workflow Variables
env:
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
HEAD_COMMIT_MSG: ${{ github.event.workflow_run.head_commit.message }}
run: |
echo "Workflow Conclusion: ${{ github.event.workflow_run.conclusion }}"
echo "Workflow Head Branch: ${{ github.event.workflow_run.head_branch }}"
echo "Workflow Head Branch: $HEAD_BRANCH"
echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
echo "Head Commit Message: ${{ github.event.workflow_run.head_commit.message }}"
echo "Head Commit Message: $HEAD_COMMIT_MSG"
echo "GitHub Ref: ${{ github.ref }}"
echo "GitHub Ref Name: ${{ github.ref_name }}"
echo "GitHub Event Name: ${{ github.event_name }}"

View File

@ -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;
}

View File

@ -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:

View File

@ -553,6 +553,44 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter
} // namespace llsd
/*****************************************************************************
* LLSDParam<std::vector<T>>
*****************************************************************************/
// Given an LLSD array, return a const std::vector<T>&, 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 <typename T>
class LLSDParam<std::vector<T>>: 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<T>(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<T>(item));
}
}
}
}
operator const std::vector<T>&() const { return v; }
private:
std::vector<T> v;
};
// Specialization for generating a hash value from an LLSD block.
namespace boost
{

View File

@ -21,6 +21,7 @@
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include "llerror.h"
#include "llevents.h"
#include "llexception.h"
#include "stringize.h"
@ -34,10 +35,34 @@ LL::WorkQueueBase::WorkQueueBase(const std::string& name, bool auto_shutdown)
: super(makeName(name))
{
if (auto_shutdown)
{
// TODO: register for "LLApp" events so we can implicitly close() on
// viewer 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()

View File

@ -53,6 +53,8 @@ namespace LL
*/
WorkQueueBase(const std::string& name, bool auto_shutdown);
virtual ~WorkQueueBase();
/**
* Since the point of WorkQueue is to pass work to some other worker
* thread(s) asynchronously, it's important that it continue to exist
@ -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;
};
/*****************************************************************************

View File

@ -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;
}

View File

@ -123,6 +123,8 @@ public:
static const std::string& badLookup(); // error string when a lookup fails
static LLSD getTypeNames();
protected:
LLFolderType() {}
~LLFolderType() {}

View File

@ -1006,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;
}
@ -1015,7 +1015,7 @@ 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())
{
@ -1041,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)
@ -1579,12 +1582,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;
@ -1596,8 +1598,6 @@ LLSD LLInventoryCategory::exportLLSD() const
{
cat_data[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
}
return cat_data;
}
bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
@ -1648,7 +1648,7 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
return true;
}
///----------------------------------------------------------------------------
/// Local function definitions
/// Local function definitions for testing purposes
///----------------------------------------------------------------------------
LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item)

View File

@ -273,7 +273,7 @@ public:
virtual bool importLegacyStream(std::istream& input_stream);
virtual bool exportLegacyStream(std::ostream& output_stream, bool include_asset_key = true) const;
LLSD exportLLSD() const;
virtual void exportLLSD(LLSD& sd) const;
bool importLLSD(const LLSD& cat_data);
//--------------------------------------------------------------------
// Member Variables
@ -288,6 +288,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<LLInventoryItem> item);
LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat);

View File

@ -1030,17 +1030,21 @@ 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)

View File

@ -447,6 +447,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

View File

@ -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)

View File

@ -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);

View File

@ -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<LLInventoryItem> create_random_inventory_item()
{
LLUUID item_id;
@ -75,6 +103,7 @@ LLPointer<LLInventoryItem> create_random_inventory_item()
sale_info,
flags,
creation);
set_random_inventory_metadata(item);
return item;
}
@ -90,6 +119,7 @@ LLPointer<LLInventoryCategory> 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<LLInventoryItem> dst = new LLInventoryItem;
bool successful_parse = dst->fromLLSD(sd);
@ -329,7 +360,9 @@ namespace tut
}
LLPointer<LLInventoryItem> src1 = create_random_inventory_item();
fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->asLLSD()) << std::endl;
LLSD sd;
src1->asLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(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<LLInventoryItem> 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<LLInventoryCategory> src1 = create_random_inventory_cat();
fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->exportLLSD()) << std::endl;
LLSD sd;
src1->exportLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(sd) << std::endl;
fileXML.close();
llifstream file(filename.c_str());
@ -488,6 +525,8 @@ 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("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
}
}

View File

@ -243,19 +243,19 @@ inline bool LLVector2::isFinite() const
}
// deprecated
inline F32 LLVector2::magVec(void) const
inline F32 LLVector2::magVec() const
{
return length();
}
// deprecated
inline F32 LLVector2::magVecSquared(void) const
inline F32 LLVector2::magVecSquared() const
{
return lengthSquared();
}
// deprecated
inline F32 LLVector2::normVec(void)
inline F32 LLVector2::normVec()
{
return normalize();
}

View File

@ -264,7 +264,7 @@ inline void LLVector3::set(const LLVector3& vec)
inline void LLVector3::set(const F32* vec)
{
set(vec[0], vec[1], vec[2]);
set(vec[VX], vec[VY], vec[VZ]);
}
inline void LLVector3::set(const glm::vec4& vec)
@ -343,7 +343,7 @@ inline F32 LLVector3::magVecSquared() const
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 &&
@ -369,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)
@ -429,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;
}
@ -477,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)

View File

@ -118,8 +118,6 @@ 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(F32 angle, const LLVector4 &vec); // Rotates about vec by angle radians
const LLVector4& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat
const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q

View File

@ -488,6 +488,7 @@ set(viewer_SOURCE_FILES
llinventorygallerymenu.cpp
llinventoryicon.cpp
llinventoryitemslist.cpp
llinventorylistener.cpp
llinventorylistitem.cpp
llinventorymodel.cpp
llinventorymodelbackgroundfetch.cpp
@ -1312,6 +1313,7 @@ set(viewer_HEADER_FILES
llinventorygallerymenu.h
llinventoryicon.h
llinventoryitemslist.h
llinventorylistener.h
llinventorylistitem.h
llinventorymodel.h
llinventorymodelbackgroundfetch.h

View File

@ -1 +1 @@
7.1.13
7.1.15

View File

@ -69,7 +69,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder )
if (!folder->areChildrenInited() || !needsSort(folder->getViewModelItem())) return;
LLFolderViewModelItemInventory* sort_modelp = static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem());
if (sort_modelp->getUUID().isNull()) return;
if (!sort_modelp->canSortContent()) return;
bool has_favorites = false;
for (std::list<LLFolderViewFolder*>::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd();

View File

@ -59,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);

View File

@ -4923,6 +4923,15 @@ 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"));
if (isFavorite())
{
items.push_back(std::string("Remove from Favorites"));
}
else
{
items.push_back(std::string("Add to Favorites"));
}
addOpenFolderMenuOptions(flags, items);
}

View File

@ -804,6 +804,7 @@ public:
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

View File

@ -199,7 +199,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);
};

View File

@ -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<uuid_vec_t>(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;
}

View File

@ -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

View File

@ -1376,6 +1376,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
{
for (auto& cat : *cat_array)
{
if (add.exceedsLimit())
{
break;
}
if(add(cat,NULL))
{
cats.push_back(cat);
@ -1394,6 +1398,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
{
for (auto& item : *item_array)
{
if (add.exceedsLimit())
{
break;
}
if(add(NULL, item))
{
items.push_back(item);
@ -3780,7 +3788,9 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
{
if (cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
{
fileXML << LLSDOStreamer<LLSDNotationFormatter>(cat->exportLLSD()) << std::endl;
LLSD sd = LLSD::emptyMap();
cat->exportLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(sd) << std::endl;
cat_count++;
}
@ -3794,7 +3804,9 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
auto it_count = items.size();
for (auto& item : items)
{
fileXML << LLSDOStreamer<LLSDNotationFormatter>(item->asLLSD()) << std::endl;
LLSD sd = LLSD::emptyMap();
item->asLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(sd) << std::endl;
if (fileXML.fail())
{

View File

@ -846,7 +846,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;
@ -1009,6 +1009,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())
@ -2755,7 +2760,8 @@ bool LLInventoryFavoritesItemsPanel::removeFavorite(const LLUUID& id, const LLIn
void LLInventoryFavoritesItemsPanel::itemChanged(const LLUUID& id, U32 mask, const LLInventoryObject* model_item)
{
if (!model_item && !getItemByID(id))
LLFolderViewItem* view_item = getItemByID(id);
if (!model_item && !view_item)
{
// remove operation, but item is not in panel already
return;
@ -2771,7 +2777,6 @@ void LLInventoryFavoritesItemsPanel::itemChanged(const LLUUID& id, U32 mask, con
// specifically exlude links and not get_is_favorite(model_item)
if (model_item && model_item->getIsFavorite())
{
LLFolderViewItem* view_item = getItemByID(id);
if (!view_item)
{
const LLViewerInventoryCategory* cat = dynamic_cast<const LLViewerInventoryCategory*>(model_item);
@ -2835,7 +2840,8 @@ void LLInventoryFavoritesItemsPanel::itemChanged(const LLUUID& id, U32 mask, con
}
}
if (!handled)
if (!handled
&& (!model_item || model_item->getParentUUID().notNull())) // filter out 'My inventory'
{
LLInventoryPanel::itemChanged(id, mask, model_item);
}

View File

@ -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();
}
}
// <FS> Extended copy & paste buttons

View File

@ -165,7 +165,6 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(
mTransparentImageAssetID( gSavedSettings.getString( "UIImgTransparentUUID" ) ), // <FS:PP> 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),
@ -190,6 +189,7 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(
mLocalTextureEnabled(false),
mInventoryPickType(pick_type)
{
setTentative(tentative);
mCanApplyImmediately = can_apply_immediately;
buildFromFile("floater_texture_ctrl.xml");
setCanMinimize(false);
@ -201,7 +201,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 +315,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 )
@ -722,7 +723,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());
@ -787,10 +788,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); // <FS:PP> 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()); // <FS:PP> FIRE-5082: "Transparent" button in Texture Panel
LLFloater::draw();
@ -843,7 +844,7 @@ void LLFloaterTexturePicker::draw()
}
// Draw Tentative Label over the image
if( mTentative && !mViewModel->isDirty() )
if( getTentative() && !mViewModel->isDirty() )
{
mTentativeLabel->setVisible( true );
drawChild(mTentativeLabel);
@ -1049,6 +1050,7 @@ void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
if (self->mOwner)
{
self->setImageID( self->getDefaultImageAssetID() );
self->setTentative(false);
}
self->commitIfImmediateSet();
}
@ -1059,6 +1061,7 @@ void LLFloaterTexturePicker::onBtnBlank(void* userdata)
LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
self->setCanApply(true, true);
self->setImageID( self->getBlankImageAssetID() );
self->setTentative(false);
self->commitIfImmediateSet();
}
@ -1079,20 +1082,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();
}*/
//<FS:Chaser> UUID texture picker
// static
@ -1344,6 +1337,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
//if (self->mSetImageAssetIDCallback)
//{
// self->mSetImageAssetIDCallback(inworld_id);
// self->setTentative(false);
//}
self->setImageID(inworld_id);
// </FS:Ansariel>
@ -1426,6 +1420,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)
@ -1446,7 +1441,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<LLUICtrl>("preview_disabled").setVisible(!can_preview && inworld_image);
getChildRef<LLUICtrl>("apply_immediate_check").setVisible(can_preview);
@ -1779,6 +1774,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te )
setCanApply(true, true);
// </FS:Ansariel>
setImageID(te.getID());
setTentative(false);
}
mNoCopyTextureSelected = false;
@ -2020,6 +2016,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;
@ -2305,6 +2312,7 @@ void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id )
if( floaterp && getEnabled() )
{
floaterp->setImageID( asset_id );
floaterp->setTentative(getTentative());
floaterp->resetDirty();
}
}

View File

@ -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;
@ -434,7 +436,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;

View File

@ -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,13 +844,11 @@ 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)

View File

@ -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 LLSD& cat_data);
void determineFolderType();
void changeType(LLFolderType::EType new_folder_type);

View File

@ -4350,18 +4350,21 @@ void LLViewerObject::boostTexturePriority(bool boost_children /* = true */)
if (isSculpted() && !isMesh())
{
LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
LLUUID sculpt_id = sculpt_params->getSculptTexture();
// <FS:minerjr>
//LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED);
// This isused to fix the textures becoming blury when object interacted with by the user and unselected.
// If this is changing the boost level for the sculpted for the first time, store the boost level before modifying it.
LLViewerFetchedTexture* sculptedTexture = LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
if (sculptedTexture->getBoostLevel() != LLGLTexture::BOOST_SELECTED)
if (sculpt_params)
{
sculptedTexture->storeBoostLevel();
}
// </FS:minerjr>
sculptedTexture->setBoostLevel(LLGLTexture::BOOST_SELECTED);
LLUUID sculpt_id = sculpt_params->getSculptTexture();
// <FS:minerjr>
//LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED);
// This isused to fix the textures becoming blury when object interacted with by the user and unselected.
// If this is changing the boost level for the sculpted for the first time, store the boost level before modifying it.
LLViewerFetchedTexture* sculptedTexture = LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
if (sculptedTexture->getBoostLevel() != LLGLTexture::BOOST_SELECTED)
{
sculptedTexture->storeBoostLevel();
}
sculptedTexture->setBoostLevel(LLGLTexture::BOOST_SELECTED);
// </FS:minerjr>
}
}
if (boost_children)

View File

@ -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;
}
@ -1377,12 +1380,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;
@ -3809,12 +3815,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;
}
}
}

View File

@ -279,7 +279,7 @@
font="SansSerifMedium"
text_color="White"
width="350">
Pressing return on an avatar attachment
Pressing enter on an avatar attachment
</text>
<combo_box
control_name="InventoryAddAttachmentBehavior"