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.xmlmaster
commit
1513d97e55
|
|
@ -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 }}"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ public:
|
|||
|
||||
static const std::string& badLookup(); // error string when a lookup fails
|
||||
|
||||
static LLSD getTypeNames();
|
||||
|
||||
protected:
|
||||
LLFolderType() {}
|
||||
~LLFolderType() {}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.13
|
||||
7.1.15
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue