Merge branch 'main' into DRTVWR-591-maint-X

# Conflicts:
#	indra/newview/app_settings/settings.xml
#	indra/newview/llinventorybridge.cpp
#	indra/newview/lltexturectrl.h
#	indra/newview/llvovolume.cpp
master
Andrey Lihatskiy 2023-10-03 19:46:29 +03:00
commit 878fb36a0b
219 changed files with 18491 additions and 6734 deletions

View File

@ -237,6 +237,7 @@ Ansariel Hiller
SL-15227
SL-15398
SL-18432
SL-19140
SL-4126
Aralara Rajal
Arare Chantilly
@ -894,6 +895,7 @@ Kitty Barnett
STORM-2149
MAINT-7581
MAINT-7081
SL-18988
Kolor Fall
Komiko Okamoto
Korvel Noh

View File

@ -21,7 +21,6 @@ set(llappearance_SOURCE_FILES
lltexglobalcolor.cpp
lltexlayer.cpp
lltexlayerparams.cpp
lltexturemanagerbridge.cpp
llwearable.cpp
llwearabledata.cpp
llwearabletype.cpp
@ -44,7 +43,6 @@ set(llappearance_HEADER_FILES
lltexglobalcolor.h
lltexlayer.h
lltexlayerparams.h
lltexturemanagerbridge.h
llwearable.h
llwearabledata.h
llwearabletype.h

View File

@ -55,7 +55,7 @@ void LLCallbackList::addFunction( callback_t func, void *data)
// only add one callback per func/data pair
//
if (containsFunction(func))
if (containsFunction(func, data))
{
return;
}

View File

@ -40,9 +40,12 @@
///----------------------------------------------------------------------------
/// Exported functions
///----------------------------------------------------------------------------
// FIXME D567 - what's the point of these, especially if we don't even use them consistently?
static const std::string INV_ITEM_ID_LABEL("item_id");
static const std::string INV_FOLDER_ID_LABEL("cat_id");
static const std::string INV_PARENT_ID_LABEL("parent_id");
static const std::string INV_THUMBNAIL_LABEL("thumbnail");
static const std::string INV_THUMBNAIL_ID_LABEL("thumbnail_id");
static const std::string INV_ASSET_TYPE_LABEL("type");
static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type");
static const std::string INV_INVENTORY_TYPE_LABEL("inv_type");
@ -99,6 +102,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other)
mParentUUID = other->mParentUUID;
mType = other->mType;
mName = other->mName;
mThumbnailUUID = other->mThumbnailUUID;
}
const LLUUID& LLInventoryObject::getUUID() const
@ -111,6 +115,11 @@ const LLUUID& LLInventoryObject::getParentUUID() const
return mParentUUID;
}
const LLUUID& LLInventoryObject::getThumbnailUUID() const
{
return mThumbnailUUID;
}
const std::string& LLInventoryObject::getName() const
{
return mName;
@ -160,6 +169,11 @@ void LLInventoryObject::setParent(const LLUUID& new_parent)
mParentUUID = new_parent;
}
void LLInventoryObject::setThumbnailUUID(const LLUUID& thumbnail_uuid)
{
mThumbnailUUID = thumbnail_uuid;
}
void LLInventoryObject::setType(LLAssetType::EType type)
{
mType = type;
@ -201,6 +215,26 @@ BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream)
{
mType = LLAssetType::lookup(valuestr);
}
else if (0 == strcmp("metadata", keyword))
{
LLSD metadata(valuestr);
if (metadata.has("thumbnail"))
{
const LLSD& thumbnail = metadata["thumbnail"];
if (thumbnail.has("asset_id"))
{
setThumbnailUUID(thumbnail["asset_id"].asUUID());
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else if(0 == strcmp("name", keyword))
{
//strcpy(valuestr, buffer + strlen(keyword) + 3);
@ -336,6 +370,7 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other)
copyObject(other);
mPermissions = other->mPermissions;
mAssetUUID = other->mAssetUUID;
mThumbnailUUID = other->mThumbnailUUID;
mDescription = other->mDescription;
mSaleInfo = other->mSaleInfo;
mInventoryType = other->mInventoryType;
@ -400,6 +435,7 @@ U32 LLInventoryItem::getCRC32() const
//LL_DEBUGS() << "8 crc: " << std::hex << crc << std::dec << LL_ENDL;
crc += (U32)mCreationDate;
//LL_DEBUGS() << "9 crc: " << std::hex << crc << std::dec << LL_ENDL;
crc += mThumbnailUUID.getCRC32();
return crc;
}
@ -655,6 +691,26 @@ BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream)
{
mType = LLAssetType::lookup(valuestr);
}
else if (0 == strcmp("metadata", keyword))
{
LLSD metadata(valuestr);
if (metadata.has("thumbnail"))
{
const LLSD& thumbnail = metadata["thumbnail"];
if (thumbnail.has("asset_id"))
{
setThumbnailUUID(thumbnail["asset_id"].asUUID());
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else if(0 == strcmp("inv_type", keyword))
{
mInventoryType = LLInventoryType::lookup(std::string(valuestr));
@ -744,6 +800,13 @@ BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL inclu
output_stream << "\t\tparent_id\t" << uuid_str << "\n";
mPermissions.exportLegacyStream(output_stream);
if (mThumbnailUUID.notNull())
{
LLSD metadata;
metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID);
output_stream << "\t\tmetadata\t" << metadata << "|\n";
}
// Check for permissions to see the asset id, and if so write it
// out as an asset id. Otherwise, apply our cheesy encryption.
if(include_asset_key)
@ -797,6 +860,11 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
sd[INV_PARENT_ID_LABEL] = mParentUUID;
sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions);
if (mThumbnailUUID.notNull())
{
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
U32 mask = mPermissions.getMaskBase();
if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
|| (mAssetUUID.isNull()))
@ -848,6 +916,35 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
{
mParentUUID = sd[w];
}
mThumbnailUUID.setNull();
w = INV_THUMBNAIL_LABEL;
if (sd.has(w))
{
const LLSD &thumbnail_map = sd[w];
w = INV_ASSET_ID_LABEL;
if (thumbnail_map.has(w))
{
mThumbnailUUID = thumbnail_map[w];
}
/* Example:
<key> asset_id </key>
<uuid> acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 </uuid>
<key> perms </key>
<integer> 8 </integer>
<key>service</key>
<integer> 3 </integer>
<key>version</key>
<integer> 1 </key>
*/
}
else
{
w = INV_THUMBNAIL_ID_LABEL;
if (sd.has(w))
{
mThumbnailUUID = sd[w].asUUID();
}
}
w = INV_PERMISSIONS_LABEL;
if (sd.has(w))
{
@ -972,135 +1069,6 @@ fail:
}
// Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML()
// because I can't find any non-test code references to it. 2009-05-04 JC
S32 LLInventoryItem::packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override) const
{
// Figure out which permissions to use.
LLPermissions perm;
if (perm_override)
{
// Use the permissions override.
perm = *perm_override;
}
else
{
// Use the current permissions.
perm = getPermissions();
}
// describe the inventory item
char* buffer = (char*) bin_bucket;
std::string creator_id_str;
perm.getCreator().toString(creator_id_str);
std::string owner_id_str;
perm.getOwner().toString(owner_id_str);
std::string last_owner_id_str;
perm.getLastOwner().toString(last_owner_id_str);
std::string group_id_str;
perm.getGroup().toString(group_id_str);
std::string asset_id_str;
getAssetUUID().toString(asset_id_str);
S32 size = sprintf(buffer, /* Flawfinder: ignore */
"%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x",
getType(),
getInventoryType(),
getName().c_str(),
creator_id_str.c_str(),
owner_id_str.c_str(),
last_owner_id_str.c_str(),
group_id_str.c_str(),
perm.getMaskBase(),
perm.getMaskOwner(),
perm.getMaskGroup(),
perm.getMaskEveryone(),
perm.getMaskNextOwner(),
asset_id_str.c_str(),
getDescription().c_str(),
getSaleInfo().getSaleType(),
getSaleInfo().getSalePrice(),
getFlags()) + 1;
return size;
}
void LLInventoryItem::unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size)
{
// Early exit on an empty binary bucket.
if (bin_bucket_size <= 1) return;
if (NULL == bin_bucket)
{
LL_ERRS() << "unpackBinaryBucket failed. bin_bucket is NULL." << LL_ENDL;
return;
}
// Convert the bin_bucket into a string.
std::vector<char> item_buffer(bin_bucket_size+1);
memcpy(&item_buffer[0], bin_bucket, bin_bucket_size); /* Flawfinder: ignore */
item_buffer[bin_bucket_size] = '\0';
std::string str(&item_buffer[0]);
LL_DEBUGS() << "item buffer: " << str << LL_ENDL;
// Tokenize the string.
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
tokenizer tokens(str, sep);
tokenizer::iterator iter = tokens.begin();
// Extract all values.
LLUUID item_id;
item_id.generate();
setUUID(item_id);
LLAssetType::EType type;
type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));
setType( type );
LLInventoryType::EType inv_type;
inv_type = (LLInventoryType::EType)(atoi((*(iter++)).c_str()));
setInventoryType( inv_type );
std::string name((*(iter++)).c_str());
rename( name );
LLUUID creator_id((*(iter++)).c_str());
LLUUID owner_id((*(iter++)).c_str());
LLUUID last_owner_id((*(iter++)).c_str());
LLUUID group_id((*(iter++)).c_str());
PermissionMask mask_base = strtoul((*(iter++)).c_str(), NULL, 16);
PermissionMask mask_owner = strtoul((*(iter++)).c_str(), NULL, 16);
PermissionMask mask_group = strtoul((*(iter++)).c_str(), NULL, 16);
PermissionMask mask_every = strtoul((*(iter++)).c_str(), NULL, 16);
PermissionMask mask_next = strtoul((*(iter++)).c_str(), NULL, 16);
LLPermissions perm;
perm.init(creator_id, owner_id, last_owner_id, group_id);
perm.initMasks(mask_base, mask_owner, mask_group, mask_every, mask_next);
setPermissions(perm);
//LL_DEBUGS() << "perm: " << perm << LL_ENDL;
LLUUID asset_id((*(iter++)).c_str());
setAssetUUID(asset_id);
std::string desc((*(iter++)).c_str());
setDescription(desc);
LLSaleInfo::EForSale sale_type;
sale_type = (LLSaleInfo::EForSale)(atoi((*(iter++)).c_str()));
S32 price = atoi((*(iter++)).c_str());
LLSaleInfo sale_info(sale_type, price);
setSaleInfo(sale_info);
U32 flags = strtoul((*(iter++)).c_str(), NULL, 16);
setFlags(flags);
time_t now = time(NULL);
setCreationDate(now);
}
///----------------------------------------------------------------------------
/// Class LLInventoryCategory
///----------------------------------------------------------------------------
@ -1150,11 +1118,32 @@ void LLInventoryCategory::setPreferredType(LLFolderType::EType type)
LLSD LLInventoryCategory::asLLSD() const
{
LLSD sd = LLSD();
sd["item_id"] = mUUID;
sd["parent_id"] = mParentUUID;
sd[INV_ITEM_ID_LABEL] = mUUID;
sd[INV_PARENT_ID_LABEL] = mParentUUID;
S8 type = static_cast<S8>(mPreferredType);
sd["type"] = type;
sd["name"] = mName;
sd[INV_ASSET_TYPE_LABEL] = type;
sd[INV_NAME_LABEL] = mName;
if (mThumbnailUUID.notNull())
{
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
return sd;
}
LLSD LLInventoryCategory::asAISCreateCatLLSD() const
{
LLSD sd = LLSD();
sd[INV_FOLDER_ID_LABEL_WS] = mUUID;
sd[INV_PARENT_ID_LABEL] = mParentUUID;
S8 type = static_cast<S8>(mPreferredType);
sd[INV_ASSET_TYPE_LABEL_WS] = type;
sd[INV_NAME_LABEL] = mName;
if (mThumbnailUUID.notNull())
{
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
return sd;
}
@ -1184,6 +1173,25 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)
{
mParentUUID = sd[w];
}
mThumbnailUUID.setNull();
w = INV_THUMBNAIL_LABEL;
if (sd.has(w))
{
const LLSD &thumbnail_map = sd[w];
w = INV_ASSET_ID_LABEL;
if (thumbnail_map.has(w))
{
mThumbnailUUID = thumbnail_map[w];
}
}
else
{
w = INV_THUMBNAIL_ID_LABEL;
if (sd.has(w))
{
mThumbnailUUID = sd[w];
}
}
w = INV_ASSET_TYPE_LABEL;
if (sd.has(w))
{
@ -1275,6 +1283,26 @@ BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream)
LLStringUtil::replaceNonstandardASCII(mName, ' ');
LLStringUtil::replaceChar(mName, '|', ' ');
}
else if (0 == strcmp("metadata", keyword))
{
LLSD metadata(valuestr);
if (metadata.has("thumbnail"))
{
const LLSD& thumbnail = metadata["thumbnail"];
if (thumbnail.has("asset_id"))
{
setThumbnailUUID(thumbnail["asset_id"].asUUID());
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else
{
setThumbnailUUID(LLUUID::null);
}
}
else
{
LL_WARNS() << "unknown keyword '" << keyword
@ -1295,6 +1323,12 @@ BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL)
output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n";
output_stream << "\t\tpref_type\t" << LLFolderType::lookup(mPreferredType) << "\n";
output_stream << "\t\tname\t" << mName.c_str() << "|\n";
if (mThumbnailUUID.notNull())
{
LLSD metadata;
metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID);
output_stream << "\t\tmetadata\t" << metadata << "|\n";
}
output_stream << "\t}\n";
return TRUE;
}
@ -1308,6 +1342,11 @@ LLSD LLInventoryCategory::exportLLSD() const
cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType);
cat_data[INV_NAME_LABEL] = mName;
if (mThumbnailUUID.notNull())
{
cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
return cat_data;
}
@ -1329,6 +1368,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
{
setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString()));
}
if (cat_data.has(INV_THUMBNAIL_LABEL))
{
LLUUID thumbnail_uuid;
const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL];
if (thumbnail_data.has(INV_ASSET_ID_LABEL))
{
thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID();
}
setThumbnailUUID(thumbnail_uuid);
}
if (cat_data.has(INV_NAME_LABEL))
{
mName = cat_data[INV_NAME_LABEL].asString();

View File

@ -70,6 +70,7 @@ public:
virtual const LLUUID& getUUID() const; // inventoryID that this item points to
virtual const LLUUID& getLinkedUUID() const; // inventoryID that this item points to, else this item's inventoryID
const LLUUID& getParentUUID() const;
virtual const LLUUID& getThumbnailUUID() const;
virtual const std::string& getName() const;
virtual LLAssetType::EType getType() const;
LLAssetType::EType getActualType() const; // bypasses indirection for linked items
@ -84,6 +85,7 @@ public:
void setUUID(const LLUUID& new_uuid);
virtual void rename(const std::string& new_name);
void setParent(const LLUUID& new_parent);
virtual void setThumbnailUUID(const LLUUID& thumbnail_uuid);
void setType(LLAssetType::EType type);
virtual void setCreationDate(time_t creation_date_utc); // only stored for items
@ -108,6 +110,7 @@ public:
protected:
LLUUID mUUID;
LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL.
LLUUID mThumbnailUUID;
LLAssetType::EType mType;
std::string mName;
time_t mCreationDate; // seconds from 1/1/1970, UTC
@ -203,9 +206,6 @@ public:
// Helper Functions
//--------------------------------------------------------------------
public:
// Pack all information needed to reconstruct this item into the given binary bucket.
S32 packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override = NULL) const;
void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size);
LLSD asLLSD() const;
void asLLSD( LLSD& sd ) const;
bool fromLLSD(const LLSD& sd, bool is_new = true);
@ -253,6 +253,7 @@ public:
LLFolderType::EType getPreferredType() const;
void setPreferredType(LLFolderType::EType type);
LLSD asLLSD() const;
LLSD asAISCreateCatLLSD() const;
bool fromLLSD(const LLSD& sd);
//--------------------------------------------------------------------

View File

@ -400,27 +400,7 @@ namespace tut
// Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML()
// because I can't find any non-test code references to it. 2009-05-04 JC
}
template<> template<>
void inventory_object::test<10>()
{
LLPointer<LLInventoryItem> src1 = create_random_inventory_item();
U8* bin_bucket = new U8[300];
S32 bin_bucket_size = src1->packBinaryBucket(bin_bucket, NULL);
LLPointer<LLInventoryItem> src2 = new LLInventoryItem();
src2->unpackBinaryBucket(bin_bucket, bin_bucket_size);
ensure_equals("1.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice());
ensure_equals("2.sale type::getSaleType() failed type", src1->getSaleInfo().getSaleType(), src2->getSaleInfo().getSaleType());
ensure_equals("3.type::getType() failed", src1->getType(), src2->getType());
ensure_equals("4.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType());
ensure_equals("5.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("6.description::getDescription() failed", src1->getDescription(), src2->getDescription());
ensure_equals("7.flags::getFlags() failed", src1->getFlags(), src2->getFlags());
}
template<> template<>
void inventory_object::test<11>()
{

View File

@ -364,7 +364,26 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced
{
LLUUID id(LLUUID::generateNewID());
LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL;
if (mPoolName == "AIS")
{
// Fetch is going to be spammy.
LL_DEBUGS("CoProcMgr", "Inventory") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName
<< "\" at "
<< mPending << LL_ENDL;
if (mPending >= (LLCoprocedureManager::DEFAULT_QUEUE_SIZE - 1))
{
// If it's all used up (not supposed to happen,
// fetched should cap it), we are going to crash
LL_WARNS("CoProcMgr", "Inventory") << "About to run out of queue space for Coprocedure(" << name
<< ") enqueuing with id=" << id.asString() << " Already pending:" << mPending << LL_ENDL;
}
}
else
{
LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at "
<< mPending << LL_ENDL;
}
auto pushed = mPendingCoprocs->try_push(boost::make_shared<QueuedCoproc>(name, id, proc));
if (pushed == boost::fibers::channel_op_status::success)
{

View File

@ -3402,6 +3402,7 @@ typedef std::map<const char*, LLMessageBuilder*> BuilderMap;
void LLMessageSystem::newMessageFast(const char *name)
{
//LL_DEBUGS("Messaging") << "creating new message: " << name << LL_ENDL;
LLMessageConfig::Flavor message_flavor =
LLMessageConfig::getMessageFlavor(name);
LLMessageConfig::Flavor server_flavor =

View File

@ -29,6 +29,7 @@ set(llrender_SOURCE_FILES
llrendertarget.cpp
llshadermgr.cpp
lltexture.cpp
lltexturemanagerbridge.cpp
lluiimage.cpp
llvertexbuffer.cpp
llglcommonfunc.cpp
@ -58,6 +59,7 @@ set(llrender_HEADER_FILES
llrendersphere.h
llshadermgr.h
lltexture.h
lltexturemanagerbridge.h
lluiimage.h
lluiimage.inl
llvertexbuffer.h

View File

@ -1023,6 +1023,20 @@ LLFontGL* LLFontGL::getFontSansSerifSmall()
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifSmallBold()
{
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",BOLD));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerifSmallItalic()
{
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",ITALIC));
return fontp;
}
//static
LLFontGL* LLFontGL::getFontSansSerif()
{

View File

@ -189,6 +189,8 @@ public:
static LLFontGL* getFontMonospace();
static LLFontGL* getFontSansSerifSmall();
static LLFontGL* getFontSansSerifSmallBold();
static LLFontGL* getFontSansSerifSmallItalic();
static LLFontGL* getFontSansSerif();
static LLFontGL* getFontSansSerifBig();
static LLFontGL* getFontSansSerifHuge();

View File

@ -27,7 +27,6 @@
#ifndef LL_TEXTUREMANAGERBRIDGE_H
#define LL_TEXTUREMANAGERBRIDGE_H
#include "llavatarappearancedefines.h"
#include "llpointer.h"
#include "llgltexture.h"

View File

@ -204,7 +204,8 @@ LLButton::LLButton(const LLButton::Params& p)
}
// Hack to make sure there is space for at least one character
if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
if (getRect().mRight >= 0 && getRect().getWidth() > 0 &&
getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
{
// Use old defaults
mLeftHPad = llbutton_orig_h_pad;

View File

@ -189,7 +189,9 @@ LLFolderView::LLFolderView(const Params& p)
mStatusTextBox(NULL),
mShowItemLinkOverlays(p.show_item_link_overlays),
mViewModel(p.view_model),
mGroupedItemModel(p.grouped_item_model)
mGroupedItemModel(p.grouped_item_model),
mForceArrange(false),
mSingleFolderMode(false)
{
LLPanel* panel = p.parent_panel;
mParentPanel = panel->getHandle();
@ -609,6 +611,7 @@ void LLFolderView::clearSelection()
}
mSelectedItems.clear();
mNeedsScroll = false;
}
std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const
@ -665,7 +668,7 @@ void LLFolderView::draw()
}
else if (mShowEmptyMessage)
{
mStatusTextBox->setValue(getFolderViewModel()->getStatusText());
mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty()));
mStatusTextBox->setVisible( TRUE );
// firstly reshape message textbox with current size. This is necessary to
@ -693,12 +696,16 @@ void LLFolderView::draw()
}
}
if (mRenameItem && mRenamer && mRenamer->getVisible() && !getVisibleRect().overlaps(mRenamer->getRect()))
{
// renamer is not connected to the item we are renaming in any form so manage it manually
// TODO: consider stopping on any scroll action instead of when out of visible area
finishRenamingItem();
}
if (mRenameItem
&& mRenamer
&& mRenamer->getVisible()
&& !getVisibleRect().overlaps(mRenamer->getRect()))
{
// renamer is not connected to the item we are renaming in any form so manage it manually
// TODO: consider stopping on any scroll action instead of when out of visible area
LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL;
finishRenamingItem();
}
// skip over LLFolderViewFolder::draw since we don't want the folder icon, label,
// and arrow for the root folder
@ -832,9 +839,12 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
mAutoOpenItems.push(item);
item->setOpen(TRUE);
if(!item->isSingleFolderMode())
{
LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);
scrollToShowItem(item, constraint_rect);
}
}
void LLFolderView::closeAutoOpenedFolders()
@ -1038,6 +1048,8 @@ void LLFolderView::paste()
// public rename functionality - can only start the process
void LLFolderView::startRenamingSelectedItem( void )
{
LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL;
// make sure selection is visible
scrollToShowSelection();
@ -1273,6 +1285,11 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask )
if(mSelectedItems.size())
{
LLFolderViewItem* last_selected = getCurSelectedItem();
if(last_selected && last_selected->isSingleFolderMode())
{
handled = FALSE;
break;
}
LLFolderViewItem* parent_folder = last_selected->getParentFolder();
if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
{
@ -1458,9 +1475,19 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
mCallbackRegistrar->popScope();
}
}
BOOL item_clicked = FALSE;
for (selected_items_t::iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
{
item_clicked |= (*item_it)->getRect().pointInRect(x, y);
}
if(!item_clicked && mSingleFolderMode)
{
clearSelection();
}
bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected();
if (menu && (handled
&& ( count > 0 && (hasVisibleChildren()) )) && // show menu only if selected items are visible
if (menu && (mSingleFolderMode || (handled
&& ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
!hide_folder_menu)
{
if (mCallbackRegistrar)
@ -1532,6 +1559,22 @@ BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
return LLView::handleHover( x, y, mask );
}
LLFolderViewItem* LLFolderView::getHoveredItem() const
{
return dynamic_cast<LLFolderViewItem*>(mHoveredItem.get());
}
void LLFolderView::setHoveredItem(LLFolderViewItem* itemp)
{
if (mHoveredItem.get() != itemp)
{
if (itemp)
mHoveredItem = itemp->getHandle();
else
mHoveredItem.markDead();
}
}
BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
@ -1716,7 +1759,7 @@ void LLFolderView::update()
mNeedsAutoSelect = FALSE;
}
BOOL is_visible = isInVisibleChain();
BOOL is_visible = isInVisibleChain() || mForceArrange;
//Puts folders/items in proper positions
// arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849)
@ -1817,13 +1860,28 @@ void LLFolderView::update()
}
}
if (mSignalSelectCallback)
{
//RN: we use keyboard focus as a proxy for user-explicit actions
BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS);
mSelectSignal(mSelectedItems, take_keyboard_focus);
}
mSignalSelectCallback = FALSE;
if (mSelectedItems.size())
{
LLFolderViewItem* item = mSelectedItems.back();
// If the goal is to show renamer, don't callback untill
// item is visible or is no longer being scrolled to.
// Otherwise renamer will be instantly closed
// Todo: consider moving renamer out of selection callback
if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible())
{
if (mSignalSelectCallback)
{
//RN: we use keyboard focus as a proxy for user-explicit actions
BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS);
mSelectSignal(mSelectedItems, take_keyboard_focus);
}
mSignalSelectCallback = FALSE;
}
}
else
{
mSignalSelectCallback = FALSE;
}
}
void LLFolderView::dumpSelectionInformation()
@ -1886,6 +1944,11 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)
flags = multi_select_flag;
}
if(mSingleFolderMode && (mSelectedItems.size() == 0))
{
buildContextMenu(*menu, flags);
}
// This adds a check for restrictions based on the entire
// selection set - for example, any one wearable may not push you
// over the limit, but all wearables together still might.
@ -2042,7 +2105,7 @@ LLFolderViewItem* LLFolderView::getNextUnselectedItem()
return new_selection;
}
S32 LLFolderView::getItemHeight()
S32 LLFolderView::getItemHeight() const
{
if(!hasVisibleChildren())
{

View File

@ -127,6 +127,9 @@ public:
bool getAllowMultiSelect() { return mAllowMultiSelect; }
bool getAllowDrag() { return mAllowDrag; }
void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; }
bool isSingleFolderMode() { return mSingleFolderMode; }
// Close all folders in the view
void closeAllFolders();
void openTopLevelFolders();
@ -136,7 +139,7 @@ public:
// Find width and height of this object and its children. Also
// makes sure that this view and its children are the right size.
virtual S32 arrange( S32* width, S32* height );
virtual S32 getItemHeight();
virtual S32 getItemHeight() const;
void arrangeAll() { mArrangeGeneration++; }
S32 getArrangeGeneration() { return mArrangeGeneration; }
@ -144,6 +147,10 @@ public:
// applies filters to control visibility of items
virtual void filter( LLFolderViewFilter& filter);
void clearHoveredItem() { setHoveredItem(nullptr); }
LLFolderViewItem* getHoveredItem() const;
void setHoveredItem(LLFolderViewItem* itemp);
// Get the last selected item
virtual LLFolderViewItem* getCurSelectedItem( void );
selected_items_t& getSelectedItems( void );
@ -237,11 +244,15 @@ public:
void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; }
void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; }
void setForceArrange(bool force) { mForceArrange = force; }
LLPanel* getParentPanel() { return mParentPanel.get(); }
// DEBUG only
void dumpSelectionInformation();
virtual S32 notify(const LLSD& info) ;
void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; }
bool useLabelSuffix() { return mUseLabelSuffix; }
virtual void updateMenu();
@ -275,6 +286,7 @@ protected:
LLHandle<LLView> mPopupMenuHandle;
std::string mMenuFileName;
LLHandle<LLView> mHoveredItem;
selected_items_t mSelectedItems;
bool mKeyboardSelection,
mAllowMultiSelect,
@ -291,7 +303,8 @@ protected:
mShowItemLinkOverlays,
mShowSelectionContext,
mShowSingleSelection,
mSuppressFolderMenu;
mSuppressFolderMenu,
mSingleFolderMode;
// Renaming variables and methods
LLFolderViewItem* mRenameItem; // The item currently being renamed
@ -330,6 +343,8 @@ protected:
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar;
bool mForceArrange;
public:
static F32 sAutoOpenTime;

View File

@ -32,6 +32,7 @@
#include "llfolderview.h"
#include "llfolderviewmodel.h"
#include "llpanel.h"
#include "llcallbacklist.h"
#include "llcriticaldamp.h"
#include "llclipboard.h"
#include "llfocusmgr.h" // gFocusMgr
@ -113,6 +114,8 @@ LLFolderViewItem::Params::Params()
icon_width("icon_width", 0),
text_pad("text_pad", 0),
text_pad_right("text_pad_right", 0),
single_folder_mode("single_folder_mode", false),
double_click_override("double_click_override", false),
arrow_size("arrow_size", 0),
max_folder_item_overlap("max_folder_item_overlap", 0)
{ }
@ -151,7 +154,9 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
mTextPad(p.text_pad),
mTextPadRight(p.text_pad_right),
mArrowSize(p.arrow_size),
mMaxFolderItemOverlap(p.max_folder_item_overlap)
mSingleFolderMode(p.single_folder_mode),
mMaxFolderItemOverlap(p.max_folder_item_overlap),
mDoubleClickOverride(p.double_click_override)
{
if (!sColorSetInitialized)
{
@ -162,7 +167,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);
sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
sColorSetInitialized = true;
}
@ -396,7 +401,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height )
// it is purely visual, so it is fine to do at our laisure
refreshSuffix();
}
mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight;
mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix) + mLabelPaddingRight;
mLabelWidthDirty = false;
}
@ -413,7 +418,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height )
return *height;
}
S32 LLFolderViewItem::getItemHeight()
S32 LLFolderViewItem::getItemHeight() const
{
return mItemHeight;
}
@ -623,11 +628,14 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
getWindow()->setCursor(UI_CURSOR_NOLOCKED);
}
root->clearHoveredItem();
return TRUE;
}
else
{
getRoot()->setShowSelectionContext(FALSE);
LLFolderView* pRoot = getRoot();
pRoot->setHoveredItem(this);
pRoot->setShowSelectionContext(FALSE);
getWindow()->setCursor(UI_CURSOR_ARROW);
// let parent handle this then...
return FALSE;
@ -682,6 +690,13 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask)
{
mIsMouseOverTitle = false;
// NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it
LLFolderView* pRoot = getRoot();
if (this == pRoot->getHoveredItem())
{
pRoot->clearHoveredItem();
}
}
BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
@ -888,7 +903,10 @@ void LLFolderViewItem::draw()
getViewModelItem()->update();
drawOpenFolderArrow(default_params, sFgColor);
if(!mSingleFolderMode)
{
drawOpenFolderArrow(default_params, sFgColor);
}
drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
@ -924,16 +942,43 @@ void LLFolderViewItem::draw()
F32 text_left = (F32)getLabelXPos();
std::string combined_string = mLabel + mLabelSuffix;
const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL);
S32 filter_offset = mViewModelItem->getFilterStringOffset();
if (filter_string_length > 0)
{
S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2;
S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
S32 top = getRect().getHeight() - TOP_PAD;
if(mLabelSuffix.empty() || (font == suffix_font))
{
S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2;
S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2;
S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
S32 top = getRect().getHeight() - TOP_PAD;
LLUIImage* box_image = default_params.selection_image;
LLRect box_rect(left, top, right, bottom);
box_image->draw(box_rect, sFilterBGColor);
}
else
{
S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
if(label_filter_length > 0)
{
S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2;
S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2;
LLUIImage* box_image = default_params.selection_image;
LLRect box_rect(left, top, right, bottom);
box_image->draw(box_rect, sFilterBGColor);
}
S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
if(suffix_filter_length > 0)
{
S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2;
S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2;
LLUIImage* box_image = default_params.selection_image;
LLRect box_rect(left, top, right, bottom);
box_image->draw(box_rect, sFilterBGColor);
}
}
}
LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor;
@ -950,7 +995,7 @@ void LLFolderViewItem::draw()
//
if (!mLabelSuffix.empty())
{
font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
S32_MAX, S32_MAX, &right_x, FALSE );
}
@ -960,12 +1005,35 @@ void LLFolderViewItem::draw()
//
if (filter_string_length > 0)
{
S32 filter_offset = mViewModelItem->getFilterStringOffset();
F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);
F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
font->renderUTF8( combined_string, filter_offset, match_string_left, yy,
if(mLabelSuffix.empty() || (font == suffix_font))
{
F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);
F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
font->renderUTF8( combined_string, filter_offset, match_string_left, yy,
sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
filter_string_length, S32_MAX, &right_x, FALSE );
}
else
{
S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
if(label_filter_length > 0)
{
F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length);
F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
font->renderUTF8( mLabel, filter_offset, match_string_left, yy,
sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, FALSE );
}
S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
if(suffix_filter_length > 0)
{
S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length);
F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, FALSE );
}
}
}
//Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to
@ -1276,7 +1344,7 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem
child_selected = TRUE;
}
}
if(openitem && child_selected)
if(openitem && child_selected && !mSingleFolderMode)
{
setOpenArrangeRecursively(TRUE);
}
@ -1701,6 +1769,11 @@ BOOL LLFolderViewFolder::isRemovable()
return TRUE;
}
void LLFolderViewFolder::destroyRoot()
{
delete this;
}
// this is an internal method used for adding items to folders.
void LLFolderViewFolder::addItem(LLFolderViewItem* item)
{
@ -1769,7 +1842,19 @@ void LLFolderViewFolder::toggleOpen()
// Force a folder open or closed
void LLFolderViewFolder::setOpen(BOOL openitem)
{
setOpenArrangeRecursively(openitem);
if(mSingleFolderMode)
{
// navigateToFolder can destroy this view
// delay it in case setOpen was called from click or key processing
doOnIdleOneTime([this]()
{
getViewModelItem()->navigateToFolder();
});
}
else
{
setOpenArrangeRecursively(openitem);
}
}
void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
@ -1972,7 +2057,8 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
}
if( !handled )
{
if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad)
if((mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad)
&& !mSingleFolderMode)
{
toggleOpen();
handled = TRUE;
@ -1990,12 +2076,45 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
{
BOOL handled = FALSE;
if(mSingleFolderMode)
{
static LLUICachedControl<bool> double_click_new_window("SingleModeDoubleClickOpenWindow", false);
if (double_click_new_window)
{
getViewModelItem()->navigateToFolder(true);
}
else
{
// navigating is going to destroy views and change children
// delay it untill handleDoubleClick processing is complete
doOnIdleOneTime([this]()
{
getViewModelItem()->navigateToFolder(false);
});
}
return TRUE;
}
if( isOpen() )
{
handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
}
if( !handled )
{
if(mDoubleClickOverride)
{
static LLUICachedControl<U32> double_click_action("MultiModeDoubleClickFolder", false);
if (double_click_action == 1)
{
getViewModelItem()->navigateToFolder(true);
return TRUE;
}
if (double_click_action == 2)
{
getViewModelItem()->navigateToFolder(false, true);
return TRUE;
}
}
if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad)
{
// don't select when user double-clicks plus sign

View File

@ -72,6 +72,8 @@ public:
text_pad_right,
arrow_size,
max_folder_item_overlap;
Optional<bool> single_folder_mode,
double_click_override;
Params();
};
@ -121,6 +123,8 @@ protected:
mIsMouseOverTitle,
mAllowWear,
mAllowDrop,
mSingleFolderMode,
mDoubleClickOverride,
mSelectPending,
mIsItemCut;
@ -174,7 +178,7 @@ public:
// Finds width and height of this object and it's children. Also
// makes sure that this view and it's children are the right size.
virtual S32 arrange( S32* width, S32* height );
virtual S32 getItemHeight();
virtual S32 getItemHeight() const;
virtual S32 getLabelXPos();
S32 getIconPad();
S32 getTextPad();
@ -213,9 +217,9 @@ public:
void setIsCurSelection(BOOL select) { mIsCurSelection = select; }
BOOL getIsCurSelection() { return mIsCurSelection; }
BOOL getIsCurSelection() const { return mIsCurSelection; }
BOOL hasVisibleChildren() { return mHasVisibleChildren; }
BOOL hasVisibleChildren() const { return mHasVisibleChildren; }
// true if object can't have children
virtual bool isFolderComplete() { return true; }
@ -264,7 +268,7 @@ public:
virtual LLFolderView* getRoot();
virtual const LLFolderView* getRoot() const;
BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor );
S32 getIndentation() { return mIndentation; }
S32 getIndentation() const { return mIndentation; }
virtual BOOL passedFilter(S32 filter_generation = -1);
virtual BOOL isPotentiallyVisible(S32 filter_generation = -1);
@ -277,6 +281,8 @@ public:
// Does not need filter update
virtual void refreshSuffix();
bool isSingleFolderMode() { return mSingleFolderMode; }
// LLView functionality
virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
@ -387,6 +393,7 @@ public:
// destroys this folder, and all children
virtual void destroyView();
void destroyRoot();
// whether known children are fully loaded (arrange sets to true)
virtual bool isFolderComplete() { return mIsFolderComplete; }

View File

@ -34,7 +34,7 @@ bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item)
return item->getSortVersion() < mTargetSortVersion;
}
std::string LLFolderViewModelCommon::getStatusText()
std::string LLFolderViewModelCommon::getStatusText(bool is_empty_folder)
{
if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())
{
@ -42,7 +42,7 @@ std::string LLFolderViewModelCommon::getStatusText()
}
else
{
return getFilter().getEmptyLookupMessage();
return getFilter().getEmptyLookupMessage(is_empty_folder);
}
}

View File

@ -69,7 +69,7 @@ public:
virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0;
virtual void setEmptyLookupMessage(const std::string& message) = 0;
virtual std::string getEmptyLookupMessage() const = 0;
virtual std::string getEmptyLookupMessage(bool is_empty_folder = false) const = 0;
virtual bool showAllResults() const = 0;
@ -125,7 +125,7 @@ public:
virtual void setFolderView(LLFolderView* folder_view) = 0;
virtual LLFolderViewFilter& getFilter() = 0;
virtual const LLFolderViewFilter& getFilter() const = 0;
virtual std::string getStatusText() = 0;
virtual std::string getStatusText(bool is_empty_folder = false) = 0;
virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;
};
@ -159,6 +159,8 @@ public:
virtual void openItem( void ) = 0;
virtual void closeItem( void ) = 0;
virtual void selectItem(void) = 0;
virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;
virtual BOOL isItemWearable() const { return FALSE; }
@ -392,7 +394,7 @@ public:
// sort everything
mTargetSortVersion++;
}
virtual std::string getStatusText();
virtual std::string getStatusText(bool is_empty_folder = false);
virtual void filter();
void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;}

View File

@ -39,7 +39,9 @@ class LLUICtrlFactory;
// Classes
//
//
// Class for diplaying named UI textures
// Do not use for displaying textures from network,
// UI textures are stored permanently!
class LLIconCtrl
: public LLUICtrl
{

View File

@ -216,7 +216,8 @@ LLLayoutStack::Params::Params()
drag_handle_first_indent("drag_handle_first_indent", 0),
drag_handle_second_indent("drag_handle_second_indent", 0),
drag_handle_thickness("drag_handle_thickness", 5),
drag_handle_shift("drag_handle_shift", 2)
drag_handle_shift("drag_handle_shift", 2),
drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody"))
{
addSynonym(border_size, "drag_handle_gap");
}
@ -236,7 +237,8 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
mDragHandleFirstIndent(p.drag_handle_first_indent),
mDragHandleSecondIndent(p.drag_handle_second_indent),
mDragHandleThickness(p.drag_handle_thickness),
mDragHandleShift(p.drag_handle_shift)
mDragHandleShift(p.drag_handle_shift),
mDragHandleColor(p.drag_handle_color())
{
}
@ -523,6 +525,15 @@ void LLLayoutStack::updateLayout()
mNeedsLayout = continue_animating;
} // end LLLayoutStack::updateLayout
void LLLayoutStack::setPanelSpacing(S32 val)
{
if (mPanelSpacing != val)
{
mPanelSpacing = val;
mNeedsLayout = true;
}
}
LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
{
if (!panelp) return NULL;
@ -576,7 +587,7 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL;
resize_bar_bg_panel_p.tab_stop = false;
resize_bar_bg_panel_p.background_visible = true;
resize_bar_bg_panel_p.bg_alpha_color = LLUIColorTable::instance().getColor("ResizebarBody");
resize_bar_bg_panel_p.bg_alpha_color = mDragHandleColor;
resize_bar_bg_panel_p.has_border = true;
resize_bar_bg_panel_p.border.border_thickness = 1;
resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight");

View File

@ -59,6 +59,8 @@ public:
Optional<S32> drag_handle_thickness;
Optional<S32> drag_handle_shift;
Optional<LLUIColor> drag_handle_color;
Params();
};
@ -89,6 +91,7 @@ public:
void updateLayout();
S32 getPanelSpacing() const { return mPanelSpacing; }
void setPanelSpacing(S32 val);
static void updateClass();
@ -128,6 +131,7 @@ private:
S32 mDragHandleSecondIndent;
S32 mDragHandleThickness;
S32 mDragHandleShift;
LLUIColor mDragHandleColor;
}; // end class LLLayoutStack

View File

@ -573,13 +573,13 @@ void LLMenuItemGL::onVisibilityChange(BOOL new_visibility)
//
// This class represents a separator.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LLMenuItemSeparatorGL::Params::Params()
{
}
LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
LLMenuItemGL( p )
{
if (p.on_visible.isProvided())
{
mVisibleSignal.connect(initEnableCallback(p.on_visible));
}
}
//virtual
@ -596,6 +596,15 @@ void LLMenuItemSeparatorGL::draw( void )
gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
}
void LLMenuItemSeparatorGL::buildDrawLabel( void )
{
if (mVisibleSignal.num_slots() > 0)
{
bool visible = mVisibleSignal(this, LLSD());
setVisible(visible);
}
}
BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
{
LLMenuGL* parent_menu = getMenu();

View File

@ -234,7 +234,9 @@ class LLMenuItemSeparatorGL : public LLMenuItemGL
public:
struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
{
Params();
Optional<EnableCallbackParam > on_visible;
Params() : on_visible("on_visible")
{}
};
LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());
@ -243,7 +245,12 @@ public:
/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
virtual void buildDrawLabel();
/*virtual*/ U32 getNominalHeight( void ) const;
private:
enable_signal_t mVisibleSignal;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -188,12 +188,12 @@ void LLScrollbar::setPageSize( S32 page_size )
}
}
BOOL LLScrollbar::isAtBeginning()
bool LLScrollbar::isAtBeginning() const
{
return mDocPos == 0;
}
BOOL LLScrollbar::isAtEnd()
bool LLScrollbar::isAtEnd() const
{
return mDocPos == getDocPosMax();
}
@ -591,7 +591,12 @@ void LLScrollbar::setValue(const LLSD& value)
BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask)
{
BOOL handled = FALSE;
if (getDocPosMax() == 0 && !getVisible())
{
return FALSE;
}
BOOL handled = FALSE;
switch( key )
{

View File

@ -105,8 +105,8 @@ public:
bool setDocPos( S32 pos, BOOL update_thumb = TRUE );
S32 getDocPos() const { return mDocPos; }
BOOL isAtBeginning();
BOOL isAtEnd();
bool isAtBeginning() const;
bool isAtEnd() const;
// Setting both at once.
void setDocParams( S32 size, S32 pos );

View File

@ -105,8 +105,8 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
LLView::addChild( mBorder );
mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 );
mInnerRect.stretch( -getBorderWidth() );
mInnerRect = getLocalRect();
mInnerRect.stretch( -getBorderWidth() );
LLRect vertical_scroll_rect = mInnerRect;
vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size;
@ -124,8 +124,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[VERTICAL] );
LLRect horizontal_scroll_rect = mInnerRect;
horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size;
LLRect horizontal_scroll_rect;
horizontal_scroll_rect.mTop = scrollbar_size;
horizontal_scroll_rect.mRight = mInnerRect.getWidth();
sbparams.name("scrollable horizontal");
sbparams.rect(horizontal_scroll_rect);
sbparams.orientation(LLScrollbar::HORIZONTAL);
@ -134,7 +135,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
sbparams.page_size(mInnerRect.getWidth());
sbparams.step_size(VERTICAL_MULTIPLE);
sbparams.visible(false);
sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT);
sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
sbparams.change_callback(p.scroll_callback);
mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[HORIZONTAL] );

View File

@ -98,8 +98,10 @@ public:
void pageDown(S32 overlap = 0);
void goToTop();
void goToBottom();
bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); }
bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); }
bool isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); }
bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); }
S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); }
S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); }
S32 getBorderWidth() const;
// LLView functionality

View File

@ -605,6 +605,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
LLButton* tab_button = getTab(index)->mButton;
gFocusMgr.setMouseCapture(this);
tab_button->setFocus(TRUE);
mMouseDownTimer.start();
}
}
if (handled) {
@ -653,7 +654,11 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
handled = LLPanel::handleHover(x, y, mask);
}
commitHoveredButton(x, y);
F32 drag_delay = 0.25f; // filter out clicks from dragging
if (mMouseDownTimer.getElapsedTimeF32() > drag_delay)
{
commitHoveredButton(x, y);
}
return handled;
}
@ -699,6 +704,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
commitHoveredButton(x, y);
mMouseDownTimer.stop();
LLPanel* cur_panel = getCurrentPanel();
if (hasMouseCapture())
{
@ -1002,7 +1008,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
}
else
{
//Scip tab button space if they are invisible(EXT - 576)
// Skip tab button space if tabs are invisible (EXT-576)
tab_panel_top = getRect().getHeight();
tab_panel_bottom = LLPANEL_BORDER_WIDTH;
}
@ -1017,9 +1023,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
}
else
{
tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH,
tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 3,
tab_panel_top,
getRect().getWidth()-LLPANEL_BORDER_WIDTH,
getRect().getWidth() - LLPANEL_BORDER_WIDTH * 2,
tab_panel_bottom );
}
child->setFollowsAll();
@ -1106,7 +1112,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
}
else
{
{
p.name("htab_"+std::string(child->getName()));
p.visible(false);
p.image_unselected(tab_img);

View File

@ -320,6 +320,7 @@ private:
LLUIColor mTabsFlashingColor;
S32 mTabIconCtrlPad;
bool mUseTabEllipses;
LLFrameTimer mMouseDownTimer;
};
#endif // LL_TABCONTAINER_H

View File

@ -163,6 +163,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
: LLPanel(p),
mHasClickCallback(p.click_callback.isProvided()),
mPadding(p.padding),
mMaxWidth(p.max_width),
mTextBox(NULL),
mInfoButton(NULL),
mPlayMediaButton(NULL),
@ -272,7 +273,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
// do this *after* we've had our size set in LLPanel::initFromParams();
const S32 REALLY_LARGE_HEIGHT = 10000;
mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT);
mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT);
if (p.styled_message.isProvided())
{
@ -288,16 +289,19 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
mTextBox->setText(p.message());
}
S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1);
updateTextBox();
snapToChildren();
}
void LLToolTip::updateTextBox()
{
S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1);
S32 text_height = mTextBox->getTextPixelHeight();
mTextBox->reshape(text_width, text_height);
if (mInfoButton)
{
LLRect text_rect = mTextBox->getRect();
LLRect icon_rect = mInfoButton->getRect();
mTextBox->translate(0, icon_rect.getCenterY() - text_rect.getCenterY());
}
}
void LLToolTip::snapToChildren()
{
// reshape tooltip panel to fit text box
LLRect tooltip_rect = calcBoundingRect();
tooltip_rect.mTop += mPadding;
@ -305,7 +309,14 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
tooltip_rect.mBottom = 0;
tooltip_rect.mLeft = 0;
mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
if (mInfoButton)
{
mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
LLRect text_rect = mTextBox->getRect();
LLRect icon_rect = mInfoButton->getRect();
mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY());
}
setShape(tooltip_rect);
}
@ -428,7 +439,10 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
}
tooltip_params.rect = LLRect (0, 1, 1, 0);
mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
if (tooltip_params.create_callback.isProvided())
mToolTip = tooltip_params.create_callback()(tooltip_params);
else
mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
gToolTipView->addChild(mToolTip);

View File

@ -68,6 +68,7 @@ public:
struct Params : public LLInitParam::Block<Params, LLPanel::Params>
{
typedef boost::function<void(void)> click_callback_t;
typedef boost::function<LLToolTip*(LLToolTip::Params)> create_callback_t;
Optional<std::string> message;
Multiple<StyledText> styled_message;
@ -84,6 +85,8 @@ public:
Optional<bool> time_based_media,
web_based_media,
media_playing;
Optional<create_callback_t> create_callback;
Optional<LLSD> create_params;
Optional<click_callback_t> click_callback,
click_playmedia_callback,
click_homepage_callback;
@ -103,11 +106,15 @@ public:
bool hasClickCallback();
LLToolTip(const Params& p);
void initFromParams(const LLToolTip::Params& params);
virtual void initFromParams(const LLToolTip::Params& params);
void getToolTipMessage(std::string & message);
private:
protected:
void updateTextBox();
void snapToChildren();
protected:
class LLTextBox* mTextBox;
class LLButton* mInfoButton;
class LLButton* mPlayMediaButton;
@ -117,6 +124,7 @@ private:
LLFrameTimer mVisibleTimer;
bool mHasClickCallback;
S32 mPadding; // pixels
S32 mMaxWidth;
};
// used for the inspector tooltips which need different background images etc.

View File

@ -531,6 +531,15 @@ void LLUICtrl::setControlVariable(LLControlVariable* control)
}
}
void LLUICtrl::removeControlVariable()
{
if (mControlVariable)
{
mControlConnection.disconnect();
mControlVariable = NULL;
}
}
//virtual
void LLUICtrl::setControlName(const std::string& control_name, LLView *context)
{

View File

@ -176,6 +176,7 @@ public:
bool setControlValue(const LLSD& value);
void setControlVariable(LLControlVariable* control);
virtual void setControlName(const std::string& control, LLView *context = NULL);
void removeControlVariable();
LLControlVariable* getControlVariable() { return mControlVariable; }

View File

@ -311,7 +311,13 @@ bool LLView::addChild(LLView* child, S32 tab_group)
}
child->mParentView = this;
updateBoundingRect();
if (getVisible() && child->getVisible())
{
// if child isn't visible it won't affect bounding rect
// if current view is not visible it will be recalculated
// on visibility change
updateBoundingRect();
}
mLastTabGroup = tab_group;
return true;
}
@ -581,6 +587,7 @@ void LLView::deleteAllChildren()
delete viewp;
mChildList.pop_front();
}
updateBoundingRect();
}
void LLView::setAllChildrenEnabled(BOOL b)
@ -879,6 +886,17 @@ LLView* LLView::childFromPoint(S32 x, S32 y, bool recur)
return 0;
}
F32 LLView::getTooltipTimeout()
{
static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f);
static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f);
// allow "scrubbing" over ui by showing next tooltip immediately
// if previous one was still visible
return (F32)(LLToolTipMgr::instance().toolTipVisible()
? tooltip_fast_delay
: tooltip_delay);
}
BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@ -888,14 +906,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
std::string tooltip = getToolTip();
if (!tooltip.empty())
{
static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f);
static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f);
static LLCachedControl<bool> allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true);
// allow "scrubbing" over ui by showing next tooltip immediately
// if previous one was still visible
F32 timeout = LLToolTipMgr::instance().toolTipVisible()
? tooltip_fast_delay
: tooltip_delay;
// Even if we don't show tooltips, consume the event, nothing below should show tooltip
if (allow_ui_tooltips)
@ -903,7 +914,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
LLToolTipMgr::instance().show(LLToolTip::Params()
.message(tooltip)
.sticky_rect(calcScreenRect())
.delay_time(timeout));
.delay_time(getTooltipTimeout()));
}
handled = TRUE;
}

View File

@ -243,6 +243,7 @@ public:
ECursorType getHoverCursor() { return mHoverCursor; }
static F32 getTooltipTimeout();
virtual const std::string getToolTip() const { return mToolTipMsg.getString(); }
void sendChildToFront(LLView* child);

View File

@ -193,6 +193,7 @@ set(viewer_SOURCE_FILES
llfloaterbuyland.cpp
llfloatercamera.cpp
llfloatercamerapresets.cpp
llfloaterchangeitemthumbnail.cpp
llfloaterchatvoicevolume.cpp
llfloaterclassified.cpp
llfloatercolorpicker.cpp
@ -229,6 +230,7 @@ set(viewer_SOURCE_FILES
llfloaterimsession.cpp
llfloaterimcontainer.cpp
llfloaterinspect.cpp
llfloaterinventorysettings.cpp
llfloaterjoystick.cpp
llfloaterlagmeter.cpp
llfloaterland.cpp
@ -244,12 +246,12 @@ set(viewer_SOURCE_FILES
llfloatermyscripts.cpp
llfloatermyenvironment.cpp
llfloaternamedesc.cpp
llfloaternewfeaturenotification.cpp
llfloaternotificationsconsole.cpp
llfloaternotificationstabbed.cpp
llfloateroutfitphotopreview.cpp
llfloaterobjectweights.cpp
llfloateropenobject.cpp
llfloatersimpleoutfitsnapshot.cpp
llfloatersimplesnapshot.cpp
llfloaterpathfindingcharacters.cpp
llfloaterpathfindingconsole.cpp
llfloaterpathfindinglinksets.cpp
@ -264,7 +266,6 @@ set(viewer_SOURCE_FILES
llfloaterpreferenceviewadvanced.cpp
llfloaterpreviewtrash.cpp
llfloaterprofiletexture.cpp
llfloaterproperties.cpp
llfloaterregiondebugconsole.cpp
llfloaterregioninfo.cpp
llfloaterreporter.cpp
@ -336,10 +337,13 @@ set(viewer_SOURCE_FILES
llinspectgroup.cpp
llinspectobject.cpp
llinspectremoteobject.cpp
llinspecttexture.cpp
llinspecttoast.cpp
llinventorybridge.cpp
llinventoryfilter.cpp
llinventoryfunctions.cpp
llinventorygallery.cpp
llinventorygallerymenu.cpp
llinventoryicon.cpp
llinventoryitemslist.cpp
llinventorylistitem.cpp
@ -565,6 +569,7 @@ set(viewer_SOURCE_FILES
lltextureinfodetails.cpp
lltexturestats.cpp
lltextureview.cpp
llthumbnailctrl.cpp
lltoast.cpp
lltoastalertpanel.cpp
lltoastgroupnotifypanel.cpp
@ -833,6 +838,7 @@ set(viewer_HEADER_FILES
llfloaterbuycurrencyhtml.h
llfloaterbuyland.h
llfloatercamerapresets.h
llfloaterchangeitemthumbnail.h
llfloatercamera.h
llfloaterchatvoicevolume.h
llfloaterclassified.h
@ -873,6 +879,7 @@ set(viewer_HEADER_FILES
llfloaterimsession.h
llfloaterimcontainer.h
llfloaterinspect.h
llfloaterinventorysettings.h
llfloaterjoystick.h
llfloaterlagmeter.h
llfloaterland.h
@ -888,12 +895,12 @@ set(viewer_HEADER_FILES
llfloatermyscripts.h
llfloatermyenvironment.h
llfloaternamedesc.h
llfloaternewfeaturenotification.h
llfloaternotificationsconsole.h
llfloaternotificationstabbed.h
llfloateroutfitphotopreview.h
llfloaterobjectweights.h
llfloateropenobject.h
llfloatersimpleoutfitsnapshot.h
llfloatersimplesnapshot.h
llfloaterpathfindingcharacters.h
llfloaterpathfindingconsole.h
llfloaterpathfindinglinksets.h
@ -908,7 +915,6 @@ set(viewer_HEADER_FILES
llfloaterpreferenceviewadvanced.h
llfloaterpreviewtrash.h
llfloaterprofiletexture.h
llfloaterproperties.h
llfloaterregiondebugconsole.h
llfloaterregioninfo.h
llfloaterreporter.h
@ -978,10 +984,13 @@ set(viewer_HEADER_FILES
llinspectgroup.h
llinspectobject.h
llinspectremoteobject.h
llinspecttexture.h
llinspecttoast.h
llinventorybridge.h
llinventoryfilter.h
llinventoryfunctions.h
llinventorygallery.h
llinventorygallerymenu.h
llinventoryicon.h
llinventoryitemslist.h
llinventorylistitem.h
@ -1198,6 +1207,7 @@ set(viewer_HEADER_FILES
lltextureinfodetails.h
lltexturestats.h
lltextureview.h
llthumbnailctrl.h
lltoast.h
lltoastalertpanel.h
lltoastgroupnotifypanel.h

View File

@ -1 +1 @@
6.6.15
6.6.16

View File

@ -3509,17 +3509,6 @@
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>InventoryInboxToggleState</key>
<map>
<key>Comment</key>
<string>Stores the open/closed state of inventory Received items panel</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>InventoryLinking</key>
<map>
@ -3994,6 +3983,28 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>LastUIFeatureVersion</key>
<map>
<key>Comment</key>
<string>UI Feature Version number for tracking feature notification between viewer builds</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<string></string>
</map>
<key>LastFindPanel</key>
<map>
<key>Comment</key>
<string>Controls which find operation appears by default when clicking "Find" button </string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>find_all_panel</string>
</map>
<key>LastName</key>
<map>
<key>Comment</key>
@ -13187,6 +13198,17 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>BatchSizeAIS3</key>
<map>
<key>Comment</key>
<string>Amount of folder ais packs into category subset request</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>20</integer>
</map>
<key>PoolSizeAIS</key>
<map>
<key>Comment</key>
@ -13194,7 +13216,7 @@
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>1</integer>
<integer>20</integer>
</map>
<key>PoolSizeUpload</key>
<map>
@ -14802,6 +14824,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FindOriginalOpenWindow</key>
<map>
<key>Comment</key>
<string>Sets the action for 'Find original' and 'Show in Inventory' (0 - shows item in main Inventory, 1 - opens a new single-folder window)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>StatsReportMaxDuration</key>
<map>
<key>Comment</key>
@ -14835,5 +14868,27 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>MultiModeDoubleClickFolder</key>
<map>
<key>Comment</key>
<string>Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 stays in current floater but switches to SFV)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>SingleModeDoubleClickOpenWindow</key>
<map>
<key>Comment</key>
<string>Sets the action for Double-click on folder in single-folder view (0 - stays in current window, 1 - opens a new window)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
</map>
</llsd>

View File

@ -120,6 +120,11 @@ const F64 CHAT_AGE_FAST_RATE = 3.0;
const F32 MIN_FIDGET_TIME = 8.f; // seconds
const F32 MAX_FIDGET_TIME = 20.f; // seconds
const S32 UI_FEATURE_VERSION = 1;
// For version 1: 1 - inventory, 2 - gltf
// Will need to change to 3 once either inventory or gltf releases and cause a conflict
const S32 UI_FEATURE_FLAGS = 1;
// The agent instance.
LLAgent gAgent;
@ -372,7 +377,7 @@ LLAgent::LLAgent() :
mHideGroupTitle(FALSE),
mGroupID(),
mInitialized(FALSE),
mInitialized(false),
mListener(),
mDoubleTapRunTimer(),
@ -448,7 +453,7 @@ LLAgent::LLAgent() :
mNextFidgetTime(0.f),
mCurrentFidget(0),
mFirstLogin(FALSE),
mFirstLogin(false),
mOutfitChosen(FALSE),
mVoiceConnected(false),
@ -505,7 +510,7 @@ void LLAgent::init()
mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT);
mInitialized = TRUE;
mInitialized = true;
}
//-----------------------------------------------------------------------------
@ -560,6 +565,93 @@ void LLAgent::onAppFocusGained()
}
}
void LLAgent::setFirstLogin(bool b)
{
mFirstLogin = b;
if (mFirstLogin)
{
// Don't notify new users about new features
if (getFeatureVersion() <= UI_FEATURE_VERSION)
{
setFeatureVersion(UI_FEATURE_VERSION, UI_FEATURE_FLAGS);
}
}
}
void LLAgent::setFeatureVersion(S32 version, S32 flags)
{
LLSD updated_version;
updated_version["version"] = version;
updated_version["flags"] = flags;
gSavedSettings.setLLSD("LastUIFeatureVersion", updated_version);
}
S32 LLAgent::getFeatureVersion()
{
S32 version;
S32 flags;
getFeatureVersionAndFlags(version, flags);
return version;
}
void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags)
{
version = 0;
flags = 0;
LLSD feature_version = gSavedSettings.getLLSD("LastUIFeatureVersion");
if (feature_version.isInteger())
{
version = feature_version.asInteger();
flags = 1; // inventory flag
}
else if (feature_version.isMap())
{
version = feature_version["version"];
flags = feature_version["flags"];
}
else if (!feature_version.isString() && !feature_version.isUndefined())
{
// is something newer inside?
version = UI_FEATURE_VERSION;
flags = UI_FEATURE_FLAGS;
}
}
void LLAgent::showLatestFeatureNotification(const std::string key)
{
S32 version;
S32 flags; // a single release can have multiple new features
getFeatureVersionAndFlags(version, flags);
if (version <= UI_FEATURE_VERSION && (flags & UI_FEATURE_FLAGS) != UI_FEATURE_FLAGS)
{
S32 flag = 0;
if (key == "inventory")
{
// Notify user about new thumbnail support
flag = 1;
}
if (key == "gltf")
{
flag = 2;
}
if ((flags & flag) == 0)
{
// Need to open on top even if called from onOpen,
// do on idle to make sure it's on top
LLSD floater_key(key);
doOnIdleOneTime([floater_key]()
{
LLFloaterReg::showInstance("new_feature_notification", floater_key);
});
setFeatureVersion(UI_FEATURE_VERSION, flags | flag);
}
}
}
void LLAgent::ageChat()
{

View File

@ -117,15 +117,20 @@ private:
//--------------------------------------------------------------------
public:
void onAppFocusGained();
void setFirstLogin(BOOL b) { mFirstLogin = b; }
void setFirstLogin(bool b);
// Return TRUE if the database reported this login as the first for this particular user.
BOOL isFirstLogin() const { return mFirstLogin; }
BOOL isInitialized() const { return mInitialized; }
bool isFirstLogin() const { return mFirstLogin; }
bool isInitialized() const { return mInitialized; }
void setFeatureVersion(S32 version, S32 flags);
S32 getFeatureVersion();
void getFeatureVersionAndFlags(S32 &version, S32 &flags);
void showLatestFeatureNotification(const std::string key);
public:
std::string mMOTD; // Message of the day
private:
BOOL mInitialized;
BOOL mFirstLogin;
bool mInitialized;
bool mFirstLogin;
boost::shared_ptr<LLAgentListener> mListener;
//--------------------------------------------------------------------

View File

@ -1306,8 +1306,9 @@ void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array
}
// Build up list of objects to be removed and items currently attached.
for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
iter != gAgentAvatarp->mAttachmentPoints.end();)
LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
LLVOAvatar::attachment_map_t::iterator end = gAgentAvatarp->mAttachmentPoints.end();
while (iter != end)
{
LLVOAvatar::attachment_map_t::iterator curiter = iter++;
LLViewerJointAttachment* attachment = curiter->second;
@ -1526,7 +1527,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
}
// static
void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id)
void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)
{
if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return;
@ -1538,7 +1539,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp);
LLAssetType::EType asset_type = wearable->getAssetType();
LLPointer<LLInventoryCallback> cb;
LLPointer<LLBoostFuncInventoryCallback> cb;
if(wear)
{
cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb);
@ -1547,6 +1548,10 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
{
cb = new LLBoostFuncInventoryCallback(wear_cb);
}
if (created_cb != NULL)
{
cb->addOnFireFunc(created_cb);
}
LLUUID folder_id;

View File

@ -129,7 +129,7 @@ protected:
//--------------------------------------------------------------------
public:
static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null);
static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function<void(const LLUUID&)> created_cb = NULL);
static void editWearable(const LLUUID& item_id);
bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,12 @@
class AISAPI
{
public:
static const S32 HTTP_TIMEOUT;
typedef enum {
INVENTORY,
LIBRARY
} ITEM_TYPE;
typedef boost::function<void(const LLUUID &invItem)> completion_t;
static bool isAvailable();
@ -50,9 +56,16 @@ public:
static void PurgeDescendents(const LLUUID &categoryId, completion_t callback = completion_t());
static void UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback = completion_t());
static void UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback = completion_t());
static void FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback = completion_t());
static void FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
static void FetchCategoryChildren(const std::string &identifier, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
static void FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
static void FetchCategorySubset(const LLUUID& catId, const uuid_vec_t specificChildren, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0);
static void FetchCOF(completion_t callback = completion_t());
static void FetchCategoryLinks(const LLUUID &catId, completion_t callback = completion_t());
static void FetchOrphans(completion_t callback = completion_t() );
static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t());
private:
typedef enum {
COPYINVENTORY,
SLAMFOLDER,
@ -61,9 +74,18 @@ private:
PURGEDESCENDENTS,
UPDATECATEGORY,
UPDATEITEM,
COPYLIBRARYCATEGORY
COPYLIBRARYCATEGORY,
CREATEINVENTORY,
FETCHITEM,
FETCHCATEGORYCHILDREN,
FETCHCATEGORYCATEGORIES,
FETCHCATEGORYSUBSET,
FETCHCOF,
FETCHORPHANS,
FETCHCATEGORYLINKS
} COMMAND_TYPE;
private:
static const std::string INVENTORY_CAP_NAME;
static const std::string LIBRARY_CAP_NAME;
@ -72,6 +94,7 @@ private:
static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc);
static void onIdle(void *userdata); // launches postponed AIS commands
static void onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body);
static std::string getInvCap();
static std::string getLibCap();
@ -87,24 +110,30 @@ private:
class AISUpdate
{
public:
AISUpdate(const LLSD& update);
AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body);
void parseUpdate(const LLSD& update);
void parseMeta(const LLSD& update);
void parseContent(const LLSD& update);
void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
void parseLink(const LLSD& link_map);
void parseLink(const LLSD& link_map, S32 depth);
void parseItem(const LLSD& link_map);
void parseCategory(const LLSD& link_map);
void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded);
void parseEmbedded(const LLSD& embedded);
void parseEmbeddedLinks(const LLSD& links);
void parseCategory(const LLSD& link_map, S32 depth);
void parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded);
void parseEmbedded(const LLSD& embedded, S32 depth);
void parseEmbeddedLinks(const LLSD& links, S32 depth);
void parseEmbeddedItems(const LLSD& items);
void parseEmbeddedCategories(const LLSD& categories);
void parseEmbeddedCategories(const LLSD& categories, S32 depth);
void parseEmbeddedItem(const LLSD& item);
void parseEmbeddedCategory(const LLSD& category);
void parseEmbeddedCategory(const LLSD& category, S32 depth);
void doUpdate();
private:
void clearParseResults();
void checkTimeout();
// Debug is very log-heavy, give it more time or it will take forever to process
// Todo: find a way to make throttle static isntead of per-request
const F32 EXPIRY_SECONDS_DEBUG = 1.f;
const F32 EXPIRY_SECONDS_LIVE = 0.008f;
typedef std::map<LLUUID,S32> uuid_int_map_t;
uuid_int_map_t mCatDescendentDeltas;
@ -113,6 +142,7 @@ private:
typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t;
deferred_item_map_t mItemsCreated;
deferred_item_map_t mItemsLost;
deferred_item_map_t mItemsUpdated;
typedef std::map<LLUUID,LLPointer<LLViewerInventoryCategory> > deferred_category_map_t;
deferred_category_map_t mCategoriesCreated;
@ -123,6 +153,10 @@ private:
uuid_list_t mObjectsDeletedIds;
uuid_list_t mItemIds;
uuid_list_t mCategoryIds;
bool mFetch;
S32 mFetchDepth;
LLTimer mTimer;
AISAPI::COMMAND_TYPE mType;
};
#endif

View File

@ -40,6 +40,7 @@
#include "llgesturemgr.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llinventoryobserver.h"
#include "llmd5.h"
#include "llnotificationsutil.h"
@ -590,6 +591,71 @@ LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOn
}
}
class LLBrokenLinkObserver : public LLInventoryObserver
{
public:
LLUUID mUUID;
bool mEnforceItemRestrictions;
bool mEnforceOrdering;
nullary_func_t mPostUpdateFunc;
LLBrokenLinkObserver(const LLUUID& uuid,
bool enforce_item_restrictions ,
bool enforce_ordering ,
nullary_func_t post_update_func) :
mUUID(uuid),
mEnforceItemRestrictions(enforce_item_restrictions),
mEnforceOrdering(enforce_ordering),
mPostUpdateFunc(post_update_func)
{
}
/* virtual */ void changed(U32 mask);
void postProcess();
};
void LLBrokenLinkObserver::changed(U32 mask)
{
if (mask & LLInventoryObserver::REBUILD)
{
// This observer should be executed after LLInventoryPanel::itemChanged(),
// but if it isn't, consider calling updateAppearanceFromCOF with a delay
const uuid_set_t& changed_item_ids = gInventory.getChangedIDs();
for (uuid_set_t::const_iterator it = changed_item_ids.begin(); it != changed_item_ids.end(); ++it)
{
const LLUUID& id = *it;
if (id == mUUID)
{
// Might not be processed yet and it is not a
// good idea to update appearane here, postpone.
doOnIdleOneTime([this]()
{
postProcess();
});
gInventory.removeObserver(this);
return;
}
}
}
}
void LLBrokenLinkObserver::postProcess()
{
LLViewerInventoryItem* item = gInventory.getItem(mUUID);
llassert(item && !item->getIsBrokenLink()); // the whole point was to get a correct link
if (item && item->getIsBrokenLink())
{
LL_INFOS_ONCE("Avatar") << "Outfit link broken despite being regenerated" << LL_ENDL;
LL_DEBUGS("Avatar", "Inventory") << "Outfit link " << mUUID << " \"" << item->getName() << "\" is broken despite being regenerated" << LL_ENDL;
}
LLAppearanceMgr::instance().updateAppearanceFromCOF(
mEnforceItemRestrictions ,
mEnforceOrdering ,
mPostUpdateFunc);
delete this;
}
struct LLFoundData
{
@ -1706,12 +1772,18 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds
{
parent_id = gInventory.getRootFolderID();
}
LLUUID subfolder_id = gInventory.createNewCategory( parent_id,
LLFolderType::FT_NONE,
src_cat->getName());
shallowCopyCategoryContents(src_id, subfolder_id, cb);
gInventory.createNewCategory(
parent_id,
LLFolderType::FT_NONE,
src_cat->getName(),
[src_id, cb](const LLUUID &new_id)
{
LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_id, new_id, cb);
gInventory.notifyObservers();
gInventory.notifyObservers();
},
src_cat->getThumbnailUUID()
);
}
void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id,
@ -2414,6 +2486,39 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL;
if (gInventory.hasPosiblyBrockenLinks())
{
// Inventory has either broken links or links that
// haven't loaded yet.
// Check if LLAppearanceMgr needs to wait.
LLUUID current_outfit_id = getCOF();
LLInventoryModel::item_array_t cof_items;
LLInventoryModel::cat_array_t cof_cats;
LLFindBrokenLinks is_brocken_link;
gInventory.collectDescendentsIf(current_outfit_id,
cof_cats,
cof_items,
LLInventoryModel::EXCLUDE_TRASH,
is_brocken_link);
if (cof_items.size() > 0)
{
// Some links haven't loaded yet, but fetch isn't complete so
// links are likely fine and we will have to wait for them to
// load
if (LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive())
{
LLBrokenLinkObserver* observer = new LLBrokenLinkObserver(cof_items.front()->getUUID(),
enforce_item_restrictions,
enforce_ordering,
post_update_func);
gInventory.addObserver(observer);
return;
}
}
}
if (enforce_item_restrictions)
{
// The point here is just to call
@ -2730,22 +2835,29 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap
{
pid = gInventory.getRootFolderID();
}
LLUUID new_cat_id = gInventory.createNewCategory(
gInventory.createNewCategory(
pid,
LLFolderType::FT_NONE,
name);
name,
[cat_id, append](const LLUUID& new_cat_id)
{
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(cat_id, cats, items);
// Create a CopyMgr that will copy items, manage its own destruction
new LLCallAfterInventoryCopyMgr(
*items, new_cat_id, std::string("wear_inventory_category_callback"),
boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
LLAppearanceMgr::getInstance(),
gInventory.getCategory(new_cat_id),
append));
// Create a CopyMgr that will copy items, manage its own destruction
new LLCallAfterInventoryCopyMgr(
*items, new_cat_id, std::string("wear_inventory_category_callback"),
boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
LLAppearanceMgr::getInstance(),
gInventory.getCategory(new_cat_id),
append));
// BAP fixes a lag in display of created dir.
gInventory.notifyObservers();
// BAP fixes a lag in display of created dir.
gInventory.notifyObservers();
},
cat->getThumbnailUUID()
);
}
else
{
@ -3203,7 +3315,7 @@ void LLAppearanceMgr::copyLibraryGestures()
// Copy gestures
LLUUID lib_gesture_cat_id =
gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false);
gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE);
if (lib_gesture_cat_id.isNull())
{
LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL;
@ -3714,7 +3826,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd
if (cofVersion == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
LL_WARNS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL;
LL_INFOS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL;
return;
}
else
@ -3986,26 +4098,15 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo
// First, make a folder in the My Outfits directory.
const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
if (AISAPI::isAvailable())
{
// cap-based category creation was buggy until recently. use
// existence of AIS as an indicator the fix is present. Does
// not actually use AIS to create the category.
inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel);
gInventory.createNewCategory(
parent_id,
LLFolderType::FT_OUTFIT,
new_folder_name,
func);
}
else
{
LLUUID folder_id = gInventory.createNewCategory(
parent_id,
LLFolderType::FT_OUTFIT,
new_folder_name);
onOutfitFolderCreated(folder_id, show_panel);
}
gInventory.createNewCategory(
parent_id,
LLFolderType::FT_OUTFIT,
new_folder_name,
[show_panel](const LLUUID &new_cat_id)
{
LLAppearanceMgr::getInstance()->onOutfitFolderCreated(new_cat_id, show_panel);
});
}
void LLAppearanceMgr::wearBaseOutfit()
@ -4338,6 +4439,73 @@ public:
~CallAfterCategoryFetchStage1()
{
}
/*virtual*/ void startFetch()
{
bool ais3 = AISAPI::isAvailable();
for (uuid_vec_t::const_iterator it = mIDs.begin(); it != mIDs.end(); ++it)
{
LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
if (!cat) continue;
if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
// CHECK IT: isCategoryComplete() checks both version and descendant count but
// fetch() only works for Unknown version and doesn't care about descentants,
// as result fetch won't start and folder will potentially get stuck as
// incomplete in observer.
// Likely either both should use only version or both should check descendants.
cat->fetch(); //blindly fetch it without seeing if anything else is fetching it.
mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer.
}
else if (!isCategoryComplete(cat))
{
LL_DEBUGS("Inventory") << "Categoty " << *it << " incomplete despite having version" << LL_ENDL;
LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
mIncomplete.push_back(*it);
}
else if (ais3)
{
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
if (items)
{
S32 complete_count = 0;
S32 incomplete_count = 0;
for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it < items->end(); ++it)
{
if (!(*it)->isFinished())
{
incomplete_count++;
}
else
{
complete_count++;
}
}
// AIS can fetch couple items, but if there
// is more than a dozen it will be very slow
// it's faster to get whole folder in such case
if (incomplete_count > LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS
|| (incomplete_count > 1 && complete_count == 0))
{
LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true);
mIncomplete.push_back(*it);
}
else
{
// let stage2 handle incomplete ones
mComplete.push_back(*it);
}
}
// else should have been handled by isCategoryComplete
}
else
{
mComplete.push_back(*it);
}
}
}
virtual void done()
{
if (mComplete.size() <= 0)
@ -4354,13 +4522,11 @@ public:
// What we do here is get the complete information on the
// items in the requested category, and set up an observer
// that will wait for that to happen.
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(mComplete.front(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
S32 count = item_array.size();
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(mComplete.front(), cats, items);
S32 count = items->size();
if(!count)
{
LL_WARNS() << "Nothing fetched in category " << mComplete.front()
@ -4372,11 +4538,13 @@ public:
return;
}
LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL;
LLViewerInventoryCategory* cat = gInventory.getCategory(mComplete.front());
S32 version = cat ? cat->getVersion() : -2;
LL_INFOS() << "stage1, category " << mComplete.front() << " got " << count << " items, version " << version << " passing to stage2 " << LL_ENDL;
uuid_vec_t ids;
for(S32 i = 0; i < count; ++i)
{
ids.push_back(item_array.at(i)->getUUID());
ids.push_back(items->at(i)->getUUID());
}
gInventory.removeObserver(this);
@ -4401,18 +4569,78 @@ protected:
nullary_func_t mCallable;
};
void callAfterCOFFetch(nullary_func_t cb)
{
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
if (AISAPI::isAvailable())
{
// Mark cof (update timer) so that background fetch won't request it
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
// For reliability assume that we have no relevant cache, so
// fetch cof along with items cof's links point to.
AISAPI::FetchCOF([cb](const LLUUID& id)
{
cb();
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
if (cat)
{
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
}
});
}
else
{
LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL;
// startup should have marked folder as fetching, remove that
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
callAfterCategoryFetch(cat_id, cb);
}
}
void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
{
CallAfterCategoryFetchStage1 *stage1 = new CallAfterCategoryFetchStage1(cat_id, cb);
stage1->startFetch();
if (stage1->isFinished())
{
stage1->done();
}
else
{
gInventory.addObserver(stage1);
}
CallAfterCategoryFetchStage1* stage1 = new CallAfterCategoryFetchStage1(cat_id, cb);
stage1->startFetch();
if (stage1->isFinished())
{
stage1->done();
}
else
{
gInventory.addObserver(stage1);
}
}
void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb)
{
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if (AISAPI::isAvailable())
{
// Mark folder (update timer) so that background fetch won't request it
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
// Assume that we have no relevant cache. Fetch folder, and items folder's links point to.
AISAPI::FetchCategoryLinks(cat_id,
[cb, cat_id](const LLUUID &id)
{
cb();
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if (cat)
{
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
}
});
}
else
{
LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL;
// startup should have marked folder as fetching, remove that
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
callAfterCategoryFetch(cat_id, cb);
}
}
void add_wearable_type_counts(const uuid_vec_t& ids,

View File

@ -338,7 +338,9 @@ public:
LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
// Invoke a given callable after category contents are fully fetched.
void callAfterCOFFetch(nullary_func_t cb);
void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb);
void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb);
// Wear all items in a uuid vector.
void wear_multiple(const uuid_vec_t& ids, bool replace);

View File

@ -214,7 +214,7 @@
#include "llcommandlineparser.h"
#include "llfloatermemleak.h"
#include "llfloaterreg.h"
#include "llfloatersimpleoutfitsnapshot.h"
#include "llfloatersimplesnapshot.h"
#include "llfloatersnapshot.h"
#include "llsidepanelinventory.h"
#include "llatmosphere.h"
@ -1526,7 +1526,7 @@ bool LLAppViewer::doFrame()
LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" )
pingMainloopTimeout("Main:Snapshot");
LLFloaterSnapshot::update(); // take snapshots
LLFloaterSimpleOutfitSnapshot::update();
LLFloaterSimpleSnapshot::update();
gGLActive = FALSE;
}

View File

@ -240,6 +240,13 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
return;
}
if (LLAppearanceMgr::instance().getCOFVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
// Wait for cof to load
LL_DEBUGS_ONCE("Avatar") << "Received atachments, but cof isn't loaded yet, postponing processing" << LL_ENDL;
return;
}
LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size()
<< " recently arrived items" << LL_ENDL;

View File

@ -730,39 +730,55 @@ namespace action_give_inventory
/**
* Checks My Inventory visibility.
*/
static bool is_give_inventory_acceptable_ids(const std::set<LLUUID> inventory_selected_uuids)
{
if (inventory_selected_uuids.empty()) return false; // nothing selected
bool acceptable = false;
std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end();
for (; it != it_end; ++it)
{
LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
// any category can be offered.
if (inv_cat)
{
acceptable = true;
continue;
}
LLViewerInventoryItem* inv_item = gInventory.getItem(*it);
// check if inventory item can be given
if (LLGiveInventory::isInventoryGiveAcceptable(inv_item))
{
acceptable = true;
continue;
}
// there are neither item nor category in inventory
acceptable = false;
break;
}
return acceptable;
}
static bool is_give_inventory_acceptable(LLInventoryPanel* panel = NULL)
{
// check selection in the panel
const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
if (inventory_selected_uuids.empty()) return false; // nothing selected
std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
if (inventory_selected_uuids.empty())
{
if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode())
{
inventory_selected_uuids.insert(panel->getRootFolderID());
}
else
{
return false; // nothing selected
}
}
bool acceptable = false;
std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end();
for (; it != it_end; ++it)
{
LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
// any category can be offered.
if (inv_cat)
{
acceptable = true;
continue;
}
LLViewerInventoryItem* inv_item = gInventory.getItem(*it);
// check if inventory item can be given
if (LLGiveInventory::isInventoryGiveAcceptable(inv_item))
{
acceptable = true;
continue;
}
// there are neither item nor category in inventory
acceptable = false;
break;
}
return acceptable;
return is_give_inventory_acceptable_ids(inventory_selected_uuids);
}
static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string)
@ -890,46 +906,65 @@ namespace action_give_inventory
* @param avatar_names - avatar names request to be sent.
* @param avatar_uuids - avatar names request to be sent.
*/
static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL)
{
llassert(avatar_names.size() == avatar_uuids.size());
const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);
if (inventory_selected_uuids.empty())
{
return;
}
static void give_inventory_ids(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, const uuid_set_t inventory_selected_uuids)
{
llassert(avatar_names.size() == avatar_uuids.size());
std::string residents;
LLAvatarActions::buildResidentsString(avatar_names, residents, true);
if (inventory_selected_uuids.empty())
{
return;
}
std::string items;
build_items_string(inventory_selected_uuids, items);
std::string residents;
LLAvatarActions::buildResidentsString(avatar_names, residents, true);
int folders_count = 0;
std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
std::string items;
build_items_string(inventory_selected_uuids, items);
//traverse through selected inventory items and count folders among them
for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it)
{
LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
if (NULL != inv_cat)
{
folders_count++;
}
}
int folders_count = 0;
std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin();
// EXP-1599
// In case of sharing multiple folders, make the confirmation
// dialog contain a warning that only one folder can be shared at a time.
std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation";
LLSD substitutions;
substitutions["RESIDENTS"] = residents;
substitutions["ITEMS"] = items;
LLShareInfo::instance().mAvatarNames = avatar_names;
LLShareInfo::instance().mAvatarUuids = avatar_uuids;
LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids));
}
//traverse through selected inventory items and count folders among them
for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it)
{
LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it);
if (NULL != inv_cat)
{
folders_count++;
}
}
// EXP-1599
// In case of sharing multiple folders, make the confirmation
// dialog contain a warning that only one folder can be shared at a time.
std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation";
LLSD substitutions;
substitutions["RESIDENTS"] = residents;
substitutions["ITEMS"] = items;
LLShareInfo::instance().mAvatarNames = avatar_names;
LLShareInfo::instance().mAvatarUuids = avatar_uuids;
LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids));
}
static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL)
{
llassert(avatar_names.size() == avatar_uuids.size());
std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);;
if (inventory_selected_uuids.empty())
{
if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode())
{
inventory_selected_uuids.insert(panel->getRootFolderID());
}
else
{
return;
}
}
give_inventory_ids(avatar_uuids, avatar_names, inventory_selected_uuids);
}
}
// static
@ -1037,6 +1072,28 @@ void LLAvatarActions::shareWithAvatars(LLView * panel)
LLNotificationsUtil::add("ShareNotification");
}
//static
void LLAvatarActions::shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater)
{
using namespace action_give_inventory;
LLFloaterAvatarPicker* picker =
LLFloaterAvatarPicker::show(boost::bind(give_inventory_ids, _1, _2, inventory_selected_uuids), TRUE, FALSE, FALSE, root_floater->getName());
if (!picker)
{
return;
}
picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable_ids, inventory_selected_uuids));
picker->openFriendsTab();
if (root_floater)
{
root_floater->addDependentFloater(picker);
}
LLNotificationsUtil::add("ShareNotification");
}
// static
bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NULL*/)
{

View File

@ -133,6 +133,7 @@ public:
* Share items with the picked avatars.
*/
static void shareWithAvatars(LLView * panel);
static void shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater);
/**
* Block/unblock the avatar by id.

View File

@ -111,6 +111,7 @@ public:
virtual void previewItem( void );
virtual void selectItem(void) { }
virtual void showProperties(void);
virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {}
// Methods used in sorting (see LLConversationSort::operator())
EConversationType const getType() const { return mConvType; }
@ -249,7 +250,7 @@ public:
bool check(const LLFolderViewModelItem* item) { return true; }
bool checkFolder(const LLFolderViewModelItem* folder) const { return true; }
void setEmptyLookupMessage(const std::string& message) { }
std::string getEmptyLookupMessage() const { return mEmpty; }
std::string getEmptyLookupMessage(bool is_empty_folder = false) const { return mEmpty; }
bool showAllResults() const { return true; }
std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; }
std::string::size_type getFilterStringSize() const { return 0; }

View File

@ -0,0 +1,956 @@
/**
* @file llfloaterchangeitemthumbnail.cpp
* @brief LLFloaterChangeItemThumbnail class implementation
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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 "llfloaterchangeitemthumbnail.h"
#include "llbutton.h"
#include "llclipboard.h"
#include "lliconctrl.h"
#include "llinventoryfunctions.h"
#include "llinventoryicon.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
#include "llfloaterreg.h"
#include "llfloatersimplesnapshot.h"
#include "lllineeditor.h"
#include "llnotificationsutil.h"
#include "lltextbox.h"
#include "lltexturectrl.h"
#include "llthumbnailctrl.h"
#include "llviewerfoldertype.h"
#include "llviewermenufile.h"
#include "llviewerobjectlist.h"
#include "llviewertexturelist.h"
#include "llwindow.h"
class LLThumbnailImagePicker : public LLFilePickerThread
{
public:
LLThumbnailImagePicker(const LLUUID &item_id);
LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id);
~LLThumbnailImagePicker();
void notify(const std::vector<std::string>& filenames) override;
private:
LLUUID mInventoryId;
LLUUID mTaskId;
};
LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id)
: LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
, mInventoryId(item_id)
{
}
LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id)
: LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
, mInventoryId(item_id)
, mTaskId(task_id)
{
}
LLThumbnailImagePicker::~LLThumbnailImagePicker()
{
}
void LLThumbnailImagePicker::notify(const std::vector<std::string>& filenames)
{
if (filenames.empty())
{
return;
}
std::string file_path = filenames[0];
if (file_path.empty())
{
return;
}
LLFloaterSimpleSnapshot::uploadThumbnail(file_path, mInventoryId, mTaskId);
}
LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key)
: LLFloater(key)
, mObserverInitialized(false)
, mTooltipState(TOOLTIP_NONE)
{
}
LLFloaterChangeItemThumbnail::~LLFloaterChangeItemThumbnail()
{
gInventory.removeObserver(this);
removeVOInventoryListener();
}
BOOL LLFloaterChangeItemThumbnail::postBuild()
{
mItemNameText = getChild<LLUICtrl>("item_name");
mItemTypeIcon = getChild<LLIconCtrl>("item_type_icon");
mThumbnailCtrl = getChild<LLThumbnailCtrl>("item_thumbnail");
mToolTipTextBox = getChild<LLTextBox>("tooltip_text");
LLSD tooltip_text;
mToolTipTextBox->setValue(tooltip_text);
LLButton *upload_local = getChild<LLButton>("upload_local");
upload_local->setClickedCallback(onUploadLocal, (void*)this);
upload_local->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_LOCAL));
upload_local->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_LOCAL));
LLButton *upload_snapshot = getChild<LLButton>("upload_snapshot");
upload_snapshot->setClickedCallback(onUploadSnapshot, (void*)this);
upload_snapshot->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT));
upload_snapshot->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT));
LLButton *use_texture = getChild<LLButton>("use_texture");
use_texture->setClickedCallback(onUseTexture, (void*)this);
use_texture->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_USE_TEXTURE));
use_texture->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_USE_TEXTURE));
mCopyToClipboardBtn = getChild<LLButton>("copy_to_clipboard");
mCopyToClipboardBtn->setClickedCallback(onCopyToClipboard, (void*)this);
mCopyToClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD));
mCopyToClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD));
mPasteFromClipboardBtn = getChild<LLButton>("paste_from_clipboard");
mPasteFromClipboardBtn->setClickedCallback(onPasteFromClipboard, (void*)this);
mPasteFromClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD));
mPasteFromClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD));
mRemoveImageBtn = getChild<LLButton>("remove_image");
mRemoveImageBtn->setClickedCallback(onRemove, (void*)this);
mRemoveImageBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_REMOVE));
mRemoveImageBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_REMOVE));
return LLFloater::postBuild();
}
void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key)
{
if (!key.has("item_id") && !key.isUUID())
{
closeFloater();
}
if (key.isUUID())
{
mItemId = key.asUUID();
}
else
{
mItemId = key["item_id"].asUUID();
mTaskId = key["task_id"].asUUID();
}
refreshFromInventory();
}
void LLFloaterChangeItemThumbnail::onFocusReceived()
{
mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
}
void LLFloaterChangeItemThumbnail::onMouseEnter(S32 x, S32 y, MASK mask)
{
mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
}
BOOL LLFloaterChangeItemThumbnail::handleDragAndDrop(
S32 x,
S32 y,
MASK mask,
BOOL drop,
EDragAndDropType cargo_type,
void *cargo_data,
EAcceptance *accept,
std::string& tooltip_msg)
{
if (cargo_type == DAD_TEXTURE)
{
LLInventoryItem *item = (LLInventoryItem *)cargo_data;
if (item->getAssetUUID().notNull())
{
if (drop)
{
assignAndValidateAsset(item->getAssetUUID());
}
*accept = ACCEPT_YES_SINGLE;
}
else
{
*accept = ACCEPT_NO;
}
}
else
{
*accept = ACCEPT_NO;
}
LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFloaterChangeItemThumbnail " << getKey() << LL_ENDL;
return TRUE;
}
void LLFloaterChangeItemThumbnail::changed(U32 mask)
{
//LLInventoryObserver
if (mTaskId.notNull() || mItemId.isNull())
{
// Task inventory or not set up yet
return;
}
const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
std::set<LLUUID>::const_iterator it;
for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
{
// set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
if (*it == mItemId)
{
// if there's a change we're interested in.
if ((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
{
refreshFromInventory();
}
}
}
}
void LLFloaterChangeItemThumbnail::inventoryChanged(LLViewerObject* object,
LLInventoryObject::object_list_t* inventory,
S32 serial_num,
void* user_data)
{
//LLVOInventoryListener
refreshFromInventory();
}
LLInventoryObject* LLFloaterChangeItemThumbnail::getInventoryObject()
{
LLInventoryObject* obj = NULL;
if (mTaskId.isNull())
{
// it is in agent inventory
if (!mObserverInitialized)
{
gInventory.addObserver(this);
mObserverInitialized = true;
}
obj = gInventory.getObject(mItemId);
}
else
{
LLViewerObject* object = gObjectList.findObject(mTaskId);
if (object)
{
if (!mObserverInitialized)
{
registerVOInventoryListener(object, NULL);
mObserverInitialized = false;
}
obj = object->getInventoryObject(mItemId);
}
}
return obj;
}
void LLFloaterChangeItemThumbnail::refreshFromInventory()
{
LLInventoryObject* obj = getInventoryObject();
if (!obj)
{
closeFloater();
}
if (obj)
{
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
bool in_trash = gInventory.isObjectDescendentOf(obj->getUUID(), trash_id);
if (in_trash && obj->getUUID() != trash_id)
{
// Close properties when moving to trash
// Aren't supposed to view properties from trash
closeFloater();
}
else
{
refreshFromObject(obj);
}
}
else
{
closeFloater();
}
}
class LLIsOutfitTextureType : public LLInventoryCollectFunctor
{
public:
LLIsOutfitTextureType() {}
virtual ~LLIsOutfitTextureType() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item);
};
bool LLIsOutfitTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
return item && (item->getType() == LLAssetType::AT_TEXTURE);
}
void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj)
{
LLUIImagePtr icon_img;
LLUUID thumbnail_id = obj->getThumbnailUUID();
LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(obj);
if (item)
{
setTitle(getString("title_item_thumbnail"));
icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE);
mRemoveImageBtn->setEnabled(thumbnail_id.notNull() && ((item->getActualType() != LLAssetType::AT_TEXTURE) || (item->getAssetUUID() != thumbnail_id)));
}
else
{
LLViewerInventoryCategory* cat = dynamic_cast<LLViewerInventoryCategory*>(obj);
if (cat)
{
setTitle(getString("title_folder_thumbnail"));
icon_img = LLUI::getUIImage(LLViewerFolderType::lookupIconName(cat->getPreferredType(), true));
if (thumbnail_id.isNull() && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))
{
// Legacy support, check if there is an image inside
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
// Not LLIsOfAssetType, because we allow links
LLIsOutfitTextureType f;
gInventory.getDirectDescendentsOf(mItemId, cats, items, f);
if (1 == items.size())
{
LLViewerInventoryItem* item = items.front();
if (item && item->getIsLinkType())
{
item = item->getLinkedItem();
}
if (item)
{
thumbnail_id = item->getAssetUUID();
if (thumbnail_id.notNull())
{
// per SL-19188, set this image as a thumbnail
LL_INFOS() << "Setting image " << thumbnail_id
<< " from outfit as a thumbnail for inventory object " << obj->getUUID()
<< LL_ENDL;
assignAndValidateAsset(thumbnail_id, true);
}
}
}
}
mRemoveImageBtn->setEnabled(thumbnail_id.notNull());
}
}
mItemTypeIcon->setImage(icon_img);
mItemNameText->setValue(obj->getName());
mThumbnailCtrl->setValue(thumbnail_id);
mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull());
mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents());
// todo: some elements might not support setting thumbnails
// since they already have them
// It is unclear how system folders should function
}
void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
(new LLThumbnailImagePicker(self->mItemId, self->mTaskId))->getFile();
LLFloater* floaterp = self->mPickerHandle.get();
if (floaterp)
{
floaterp->closeFloater();
}
floaterp = self->mSnapshotHandle.get();
if (floaterp)
{
floaterp->closeFloater();
}
}
void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
LLFloater* floaterp = self->mSnapshotHandle.get();
// Show the dialog
if (floaterp)
{
floaterp->openFloater();
}
else
{
LLSD key;
key["item_id"] = self->mItemId;
key["task_id"] = self->mTaskId;
LLFloaterSimpleSnapshot* snapshot_floater = (LLFloaterSimpleSnapshot*)LLFloaterReg::showInstance("simple_snapshot", key, true);
if (snapshot_floater)
{
self->addDependentFloater(snapshot_floater);
self->mSnapshotHandle = snapshot_floater->getHandle();
snapshot_floater->setOwner(self);
}
}
floaterp = self->mPickerHandle.get();
if (floaterp)
{
floaterp->closeFloater();
}
}
void LLFloaterChangeItemThumbnail::onUseTexture(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
LLInventoryObject* obj = self->getInventoryObject();
if (obj)
{
self->showTexturePicker(obj->getThumbnailUUID());
}
LLFloater* floaterp = self->mSnapshotHandle.get();
if (floaterp)
{
floaterp->closeFloater();
}
}
void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
LLInventoryObject* obj = self->getInventoryObject();
if (obj)
{
LLClipboard::instance().reset();
LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE);
self->mPasteFromClipboardBtn->setEnabled(true);
}
}
void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
std::vector<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
if (objects.size() > 0)
{
LLUUID potential_uuid = objects[0];
LLUUID asset_id;
if (potential_uuid.notNull())
{
LLViewerInventoryItem* item = gInventory.getItem(potential_uuid);
if (item)
{
// no point checking snapshot?
if (item->getType() == LLAssetType::AT_TEXTURE)
{
bool copy = item->getPermissions().allowCopyBy(gAgent.getID());
bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID());
if (copy && xfer)
{
asset_id = item->getAssetUUID();
}
else
{
LLNotificationsUtil::add("ThumbnailInsufficientPermissions");
return;
}
}
}
else
{
// assume that this is a texture
asset_id = potential_uuid;
}
}
LLInventoryObject* obj = self->getInventoryObject();
if (obj && obj->getThumbnailUUID() == asset_id)
{
// nothing to do
return;
}
if (asset_id.notNull())
{
self->assignAndValidateAsset(asset_id);
}
// else show 'buffer has no texture' warning?
}
}
void LLFloaterChangeItemThumbnail::onRemove(void *userdata)
{
LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
LLSD payload;
payload["item_id"] = self->mItemId;
payload["object_id"] = self->mTaskId;
LLNotificationsUtil::add("DeleteThumbnail", LLSD(), payload, boost::bind(&LLFloaterChangeItemThumbnail::onRemovalConfirmation, _1, _2, self->getHandle()));
}
// static
void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0 && !handle.isDead() && !handle.get()->isDead())
{
LLFloaterChangeItemThumbnail* self = (LLFloaterChangeItemThumbnail*)handle.get();
self->setThumbnailId(LLUUID::null);
}
}
struct ImageLoadedData
{
LLUUID mThumbnailId;
LLUUID mObjectId;
LLHandle<LLFloater> mFloaterHandle;
bool mSilent;
// Keep image reference to prevent deletion on timeout
LLPointer<LLViewerFetchedTexture> mTexturep;
};
void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent)
{
LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::getFetchedTexture(asset_id);
if (texturep->isMissingAsset())
{
LL_WARNS() << "Attempted to assign missing asset " << asset_id << LL_ENDL;
if (!silent)
{
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
}
}
else if (texturep->getFullWidth() == 0)
{
if (silent)
{
mExpectingAssetId = LLUUID::null;
}
else
{
// don't warn user multiple times if some textures took their time
mExpectingAssetId = asset_id;
}
ImageLoadedData *data = new ImageLoadedData();
data->mObjectId = mItemId;
data->mThumbnailId = asset_id;
data->mFloaterHandle = getHandle();
data->mSilent = silent;
data->mTexturep = texturep;
texturep->setLoadedCallback(onImageDataLoaded,
MAX_DISCARD_LEVEL, // Don't need full image, just size data
FALSE,
FALSE,
(void*)data,
NULL,
FALSE);
}
else
{
if (validateAsset(asset_id))
{
setThumbnailId(asset_id);
}
else if (!silent)
{
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
}
}
}
bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id)
{
if (asset_id.isNull())
{
return false;
}
LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
if (!texturep)
{
return false;
}
if (texturep->isMissingAsset())
{
return false;
}
if (texturep->getFullWidth() != texturep->getFullHeight())
{
return false;
}
if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX
|| texturep->getFullHeight() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX)
{
return false;
}
if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN
|| texturep->getFullHeight() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN)
{
return false;
}
return true;
}
//static
void LLFloaterChangeItemThumbnail::onImageDataLoaded(
BOOL success,
LLViewerFetchedTexture *src_vi,
LLImageRaw* src,
LLImageRaw* aux_src,
S32 discard_level,
BOOL final,
void* userdata)
{
if (!userdata) return;
if (!final && success) return; //not done yet
ImageLoadedData* data = (ImageLoadedData*)userdata;
if (success)
{
// Update the item, set it even if floater is dead
if (validateAsset(data->mThumbnailId))
{
setThumbnailId(data->mThumbnailId, data->mObjectId);
}
else if (!data->mSilent)
{
// Should this only appear if floater is alive?
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
}
}
// Update floater
if (!data->mSilent && !data->mFloaterHandle.isDead())
{
LLFloaterChangeItemThumbnail* self = static_cast<LLFloaterChangeItemThumbnail*>(data->mFloaterHandle.get());
if (self && self->mExpectingAssetId == data->mThumbnailId)
{
self->mExpectingAssetId = LLUUID::null;
}
}
delete data;
}
//static
void LLFloaterChangeItemThumbnail::onFullImageLoaded(
BOOL success,
LLViewerFetchedTexture* src_vi,
LLImageRaw* src,
LLImageRaw* aux_src,
S32 discard_level,
BOOL final,
void* userdata)
{
if (!userdata) return;
if (!final && success) return; //not done yet
ImageLoadedData* data = (ImageLoadedData*)userdata;
if (success)
{
if (src_vi->getFullWidth() != src_vi->getFullHeight()
|| src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN)
{
if (!data->mSilent)
{
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
}
}
else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX)
{
LLFloaterSimpleSnapshot::uploadThumbnail(src, data->mObjectId, LLUUID::null);
}
else
{
setThumbnailId(data->mThumbnailId, data->mObjectId);
}
}
delete data;
}
void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id)
{
// show hourglass cursor when loading inventory window
getWindow()->setCursor(UI_CURSOR_WAIT);
LLFloater* floaterp = mPickerHandle.get();
// Show the dialog
if (floaterp)
{
floaterp->openFloater();
}
else
{
floaterp = new LLFloaterTexturePicker(
this,
thumbnail_id,
thumbnail_id,
thumbnail_id,
FALSE,
TRUE,
"SELECT PHOTO",
PERM_NONE,
PERM_NONE,
PERM_NONE,
FALSE,
NULL);
mPickerHandle = floaterp->getHandle();
LLFloaterTexturePicker* texture_floaterp = dynamic_cast<LLFloaterTexturePicker*>(floaterp);
if (texture_floaterp)
{
//texture_floaterp->setTextureSelectedCallback();
//texture_floaterp->setOnUpdateImageStatsCallback();
texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&)
{
if (op == LLTextureCtrl::TEXTURE_SELECT)
{
onTexturePickerCommit();
}
}
);
texture_floaterp->setLocalTextureEnabled(FALSE);
texture_floaterp->setBakeTextureEnabled(FALSE);
texture_floaterp->setCanApplyImmediately(false);
texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/);
texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN);
addDependentFloater(texture_floaterp);
}
floaterp->openFloater();
}
floaterp->setFocus(TRUE);
}
void LLFloaterChangeItemThumbnail::onTexturePickerCommit()
{
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mPickerHandle.get();
if (floaterp)
{
LLUUID asset_id = floaterp->getAssetID();
if (asset_id.isNull())
{
setThumbnailId(asset_id);
return;
}
LLInventoryObject* obj = getInventoryObject();
if (obj && obj->getThumbnailUUID() == asset_id)
{
// nothing to do
return;
}
LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD);
if (!texturep)
{
LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL;
return;
}
if (texturep->isMissingAsset())
{
LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL;
return;
}
if (texturep->getFullWidth() != texturep->getFullHeight())
{
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
return;
}
if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN
&& texturep->getFullWidth() > 0)
{
LLNotificationsUtil::add("ThumbnailDimentionsLimit");
return;
}
if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX
|| texturep->getFullWidth() == 0)
{
if (texturep->isFullyLoaded()
&& (texturep->getCachedRawImageLevel() == 0 || texturep->getRawImageLevel() == 0)
&& (texturep->isCachedRawImageReady() || texturep->isRawImageValid()))
{
if (texturep->isRawImageValid())
{
LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), mItemId, mTaskId);
}
else
{
LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getCachedRawImage(), mItemId, mTaskId);
}
}
else
{
ImageLoadedData* data = new ImageLoadedData();
data->mObjectId = mItemId;
data->mThumbnailId = asset_id;
data->mFloaterHandle = getHandle();
data->mSilent = false;
data->mTexturep = texturep;
texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
texturep->setMinDiscardLevel(0);
texturep->setLoadedCallback(onFullImageLoaded,
0, // Need best quality
TRUE,
FALSE,
(void*)data,
NULL,
FALSE);
texturep->forceToSaveRawImage(0);
}
return;
}
setThumbnailId(asset_id);
}
}
void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID &new_thumbnail_id)
{
LLInventoryObject* obj = getInventoryObject();
if (!obj)
{
return;
}
if (mTaskId.notNull())
{
LL_ERRS() << "Not implemented yet" << LL_ENDL;
return;
}
setThumbnailId(new_thumbnail_id, mItemId, obj);
}
void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id)
{
LLInventoryObject* obj = gInventory.getObject(object_id);
if (!obj)
{
return;
}
setThumbnailId(new_thumbnail_id, object_id, obj);
}
void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj)
{
if (obj->getThumbnailUUID() != new_thumbnail_id)
{
LLSD updates;
if (new_thumbnail_id.notNull())
{
// At the moment server expects id as a string
updates["thumbnail"] = LLSD().with("asset_id", new_thumbnail_id.asString());
}
else
{
// No thumbnail isntead of 'null id thumbnail'
updates["thumbnail"] = LLSD();
}
LLViewerInventoryCategory* view_folder = dynamic_cast<LLViewerInventoryCategory*>(obj);
if (view_folder)
{
update_inventory_category(object_id, updates, NULL);
}
LLViewerInventoryItem* view_item = dynamic_cast<LLViewerInventoryItem*>(obj);
if (view_item)
{
update_inventory_item(object_id, updates, NULL);
}
}
}
void LLFloaterChangeItemThumbnail::onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state)
{
mTooltipState = state;
std::string tooltip_text;
std::string tooltip_name = "tooltip_" + button->getName();
if (hasString(tooltip_name))
{
tooltip_text = getString(tooltip_name);
}
mToolTipTextBox->setValue(tooltip_text);
}
void LLFloaterChangeItemThumbnail::onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state)
{
if (mTooltipState == state)
{
mTooltipState = TOOLTIP_NONE;
LLSD tooltip_text;
mToolTipTextBox->setValue(tooltip_text);
}
}

View File

@ -0,0 +1,139 @@
/**
* @file llfloaterchangeitemthumbnail.h
* @brief LLFloaterChangeItemThumbnail class definition
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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_LLFLOATERCHANGEITEMTHUMBNAIL_H
#define LL_LLFLOATERCHANGEITEMTHUMBNAIL_H
#include "llfloater.h"
#include "llinventoryobserver.h"
#include "llvoinventorylistener.h"
class LLButton;
class LLIconCtrl;
class LLTextBox;
class LLThumbnailCtrl;
class LLUICtrl;
class LLViewerInventoryItem;
class LLViewerFetchedTexture;
class LLFloaterChangeItemThumbnail : public LLFloater, public LLInventoryObserver, public LLVOInventoryListener
{
public:
LLFloaterChangeItemThumbnail(const LLSD& key);
~LLFloaterChangeItemThumbnail();
BOOL postBuild() override;
void onOpen(const LLSD& key) override;
void onFocusReceived() override;
void onMouseEnter(S32 x, S32 y, MASK mask) override;
BOOL handleDragAndDrop(
S32 x,
S32 y,
MASK mask,
BOOL drop,
EDragAndDropType cargo_type,
void *cargo_data,
EAcceptance *accept,
std::string& tooltip_msg) override;
void changed(U32 mask) override;
void inventoryChanged(LLViewerObject* object,
LLInventoryObject::object_list_t* inventory,
S32 serial_num,
void* user_data) override;
static bool validateAsset(const LLUUID &asset_id);
private:
LLInventoryObject* getInventoryObject();
void refreshFromInventory();
void refreshFromObject(LLInventoryObject* obj);
static void onUploadLocal(void*);
static void onUploadSnapshot(void*);
static void onUseTexture(void*);
static void onCopyToClipboard(void*);
static void onPasteFromClipboard(void*);
static void onRemove(void*);
static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle);
void assignAndValidateAsset(const LLUUID &asset_id, bool silent = false);
static void onImageDataLoaded(BOOL success,
LLViewerFetchedTexture *src_vi,
LLImageRaw* src,
LLImageRaw* aux_src,
S32 discard_level,
BOOL final,
void* userdata);
static void onFullImageLoaded(BOOL success,
LLViewerFetchedTexture* src_vi,
LLImageRaw* src,
LLImageRaw* aux_src,
S32 discard_level,
BOOL final,
void* userdata);
void showTexturePicker(const LLUUID &thumbnail_id);
void onTexturePickerCommit();
void setThumbnailId(const LLUUID &new_thumbnail_id);
static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id);
static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj);
enum EToolTipState
{
TOOLTIP_NONE,
TOOLTIP_UPLOAD_LOCAL,
TOOLTIP_UPLOAD_SNAPSHOT,
TOOLTIP_USE_TEXTURE,
TOOLTIP_COPY_TO_CLIPBOARD,
TOOLTIP_COPY_FROM_CLIPBOARD,
TOOLTIP_REMOVE,
};
void onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state);
void onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state);
bool mObserverInitialized;
EToolTipState mTooltipState;
LLUUID mItemId;
LLUUID mTaskId;
LLUUID mExpectingAssetId;
LLIconCtrl *mItemTypeIcon;
LLUICtrl *mItemNameText;
LLThumbnailCtrl *mThumbnailCtrl;
LLTextBox *mToolTipTextBox;
LLButton *mCopyToClipboardBtn;
LLButton *mPasteFromClipboardBtn;
LLButton *mRemoveImageBtn;
LLHandle<LLFloater> mPickerHandle;
LLHandle<LLFloater> mSnapshotHandle;
};
#endif // LL_LLFLOATERCHANGEITEMTHUMBNAIL_H

View File

@ -260,7 +260,7 @@ void LLFloaterEditEnvironmentBase::onSaveAsCommit(const LLSD& notification, cons
}
else if (mInventoryItem)
{
const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
LLUUID parent_id = mInventoryItem->getParentUUID();
if (marketplacelistings_id == parent_id || gInventory.isObjectDescendentOf(mInventoryItem->getUUID(), gInventory.getLibraryRootFolderID()))
{

View File

@ -164,6 +164,12 @@ bool LLFloaterForgetUser::onConfirmLogout(const LLSD& notification, const LLSD&
if (option == 0)
{
// Remove creds
std::string grid_id = LLGridManager::getInstance()->getGridId(grid);
if (grid_id.empty())
{
grid_id = grid;
}
gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, LLStartUp::getUserId()); // doesn't write
gSecAPIHandler->removeFromCredentialMap("login_list", grid, LLStartUp::getUserId());
LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid);
@ -228,7 +234,13 @@ void LLFloaterForgetUser::processForgetUser()
void LLFloaterForgetUser::forgetUser(const std::string &userid, const std::string &fav_id, const std::string &grid, bool delete_data)
{
// Remove creds
gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid);
std::string grid_id = LLGridManager::getInstance()->getGridId(grid);
if (grid_id.empty())
{
grid_id = grid;
}
gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, userid); // doesn't write
gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); // write operation
LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid);
if (cred.notNull() && cred->userID() == userid)

View File

@ -211,7 +211,7 @@ BOOL LLFloaterGesture::postBuild()
getChildView("play_btn")->setVisible( true);
getChildView("stop_btn")->setVisible( false);
setDefaultBtn("play_btn");
mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false);
mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
uuid_vec_t folders;
folders.push_back(mGestureFolderID);

View File

@ -0,0 +1,44 @@
/**
* @file llfloaterinventorysettings.cpp
* @brief LLFloaterInventorySettings class implementation
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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 "llfloaterinventorysettings.h"
LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key)
: LLFloater(key)
{
}
LLFloaterInventorySettings::~LLFloaterInventorySettings()
{}
BOOL LLFloaterInventorySettings::postBuild()
{
getChild<LLButton>("ok_btn")->setCommitCallback(boost::bind(&LLFloater::closeFloater, this, false));
return TRUE;
}

View File

@ -0,0 +1,45 @@
/**
* @file llfloaterinventorysettings.h
* @brief LLFloaterInventorySettings class definition
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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_LLFLOATERINVENTORYSETTINGS_H
#define LL_LLFLOATERINVENTORYSETTINGS_H
#include "llfloater.h"
class LLFloaterInventorySettings
: public LLFloater
{
friend class LLFloaterReg;
public:
virtual BOOL postBuild();
private:
LLFloaterInventorySettings(const LLSD& key);
~LLFloaterInventorySettings();
};
#endif

View File

@ -335,8 +335,8 @@ BOOL LLFloaterLinkReplace::tick()
void LLFloaterLinkReplace::processBatch(LLInventoryModel::item_array_t items)
{
const LLViewerInventoryItem* target_item = gInventory.getItem(mTargetUUID);
const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it)
{

View File

@ -41,8 +41,11 @@
#include "llnotificationmanager.h"
#include "llnotificationsutil.h"
#include "llsidepaneliteminfo.h"
#include "llsidepaneltaskinfo.h"
#include "lltabcontainer.h"
#include "lltextbox.h"
#include "lltrans.h"
#include "llviewerwindow.h"
///----------------------------------------------------------------------------
/// LLPanelMarketplaceListings
@ -227,18 +230,31 @@ void LLPanelMarketplaceListings::onTabChange()
void LLPanelMarketplaceListings::onAddButtonClicked()
{
// Find active panel
LLInventoryPanel* panel = (LLInventoryPanel*)getChild<LLTabContainer>("marketplace_filter_tabs")->getCurrentPanel();
if (panel)
{
LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
llassert(marketplacelistings_id.notNull());
LLFolderType::EType preferred_type = LLFolderType::lookup("category");
LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null);
gInventory.notifyObservers();
panel->setSelectionByID(category, TRUE);
panel->getRootFolder()->setNeedsAutoRename(TRUE);
LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
llassert(marketplacelistings_id.notNull());
LLFolderType::EType preferred_type = LLFolderType::lookup("category");
LLHandle<LLPanel> handle = getHandle();
gInventory.createNewCategory(
marketplacelistings_id,
preferred_type,
LLStringUtil::null,
[handle](const LLUUID &new_cat_id)
{
// Find active panel
LLPanel *marketplace_panel = handle.get();
if (!marketplace_panel)
{
return;
}
LLInventoryPanel* panel = (LLInventoryPanel*)marketplace_panel->getChild<LLTabContainer>("marketplace_filter_tabs")->getCurrentPanel();
if (panel)
{
gInventory.notifyObservers();
panel->setSelectionByID(new_cat_id, TRUE);
panel->getRootFolder()->setNeedsAutoRename(TRUE);
}
}
);
}
void LLPanelMarketplaceListings::onAuditButtonClicked()
@ -359,6 +375,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key)
, mInventoryTitle(NULL)
, mPanelListings(NULL)
, mPanelListingsSet(false)
, mRootFolderCreating(false)
{
}
@ -431,7 +448,7 @@ void LLFloaterMarketplaceListings::fetchContents()
{
LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this));
LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING);
LLInventoryModelBackgroundFetch::instance().start(mRootFolderId);
LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true);
LLMarketplaceData::instance().getSLMListings();
}
}
@ -444,15 +461,50 @@ void LLFloaterMarketplaceListings::setRootFolder()
// If we are *not* a merchant or we have no market place connection established yet, do nothing
return;
}
if (!gInventory.isInventoryUsable())
{
return;
}
LLFolderType::EType preferred_type = LLFolderType::FT_MARKETPLACE_LISTINGS;
// We are a merchant. Get the Marketplace listings folder, create it if needs be.
LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true);
if (marketplacelistings_id.isNull())
{
// We should never get there unless the inventory fails badly
LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL;
return;
}
LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(preferred_type);
if (marketplacelistings_id.isNull())
{
if (!mRootFolderCreating)
{
mRootFolderCreating = true;
gInventory.createNewCategory(
gInventory.getRootFolderID(),
preferred_type,
LLStringUtil::null,
[](const LLUUID &new_cat_id)
{
LLFloaterMarketplaceListings* marketplace = LLFloaterReg::findTypedInstance<LLFloaterMarketplaceListings>("marketplace_listings");
if (marketplace)
{
if (new_cat_id.notNull())
{
// will call setRootFolder again
marketplace->updateView();
}
// don't update in case of failure, createNewCategory can return
// immediately if cap is missing and will cause a loop
else
{
// unblock
marketplace->mRootFolderCreating = false;
LL_WARNS("SLM") << "Inventory warning: Failed to create marketplace listings folder for a merchant" << LL_ENDL;
}
}
}
);
}
return;
}
mRootFolderCreating = false;
// No longer need to observe new category creation
if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver))
@ -540,6 +592,11 @@ void LLFloaterMarketplaceListings::updateView()
{
setRootFolder();
}
if (mRootFolderCreating)
{
// waiting for callback
return;
}
// Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading
if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) )
@ -843,14 +900,17 @@ void LLFloaterMarketplaceValidation::onOpen(const LLSD& key)
LLUUID cat_id(key.asUUID());
if (cat_id.isNull())
{
cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
}
// Validates the folder
if (cat_id.notNull())
{
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false);
LLMarketplaceValidator::getInstance()->validateMarketplaceListings(
cat_id,
NULL,
boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3),
false);
}
// Handle the listing folder being processed
@ -954,18 +1014,44 @@ LLFloaterItemProperties::~LLFloaterItemProperties()
BOOL LLFloaterItemProperties::postBuild()
{
// On the standalone properties floater, we have no need for a back button...
LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
LLButton* back_btn = panel->getChild<LLButton>("back_btn");
back_btn->setVisible(FALSE);
return LLFloater::postBuild();
}
void LLFloaterItemProperties::onOpen(const LLSD& key)
{
// Tell the panel which item it needs to visualize
LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel");
panel->setItemID(key["id"].asUUID());
LLPanel* panel = findChild<LLPanel>("sidepanel");
LLSidepanelItemInfo* item_panel = dynamic_cast<LLSidepanelItemInfo*>(panel);
if (item_panel)
{
item_panel->setItemID(key["id"].asUUID());
if (key.has("object"))
{
item_panel->setObjectID(key["object"].asUUID());
}
item_panel->setParentFloater(this);
}
LLSidepanelTaskInfo* task_panel = dynamic_cast<LLSidepanelTaskInfo*>(panel);
if (task_panel)
{
task_panel->setObjectSelection(LLSelectMgr::getInstance()->getSelection());
}
}
LLMultiItemProperties::LLMultiItemProperties(const LLSD& key)
: LLMultiFloater(LLSD())
{
// start with a small rect in the top-left corner ; will get resized
LLRect rect;
rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 350, 350);
setRect(rect);
LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup(key.asString());
if (last_floater)
{
stackWith(*last_floater);
}
setTitle(LLTrans::getString("MultiPropertiesTitle"));
buildTabContainer();
}

View File

@ -33,6 +33,7 @@
#include "llinventorypanel.h"
#include "llnotificationptr.h"
#include "llmodaldialog.h"
#include "llmultifloater.h"
#include "lltexteditor.h"
class LLInventoryCategoriesObserver;
@ -139,6 +140,7 @@ private:
LLTextBox * mInventoryTitle;
LLUUID mRootFolderId;
bool mRootFolderCreating;
LLPanelMarketplaceListings * mPanelListings;
bool mPanelListingsSet;
};
@ -223,4 +225,10 @@ public:
private:
};
class LLMultiItemProperties : public LLMultiFloater
{
public:
LLMultiItemProperties(const LLSD& key);
};
#endif // LL_LLFLOATERMARKETPLACELISTINGS_H

View File

@ -0,0 +1,76 @@
/**
* @file llfloaternewfeaturenotification.cpp
* @brief LLFloaterNewFeatureNotification class implementation
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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 "llfloaternewfeaturenotification.h"
LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key)
: LLFloater(key)
{
}
LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification()
{
}
BOOL LLFloaterNewFeatureNotification::postBuild()
{
setCanDrag(FALSE);
getChild<LLButton>("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this));
const std::string title_txt = "title_txt";
const std::string dsc_txt = "description_txt";
std::string feature = "_" + getKey().asString();
getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature));
getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature));
return TRUE;
}
void LLFloaterNewFeatureNotification::onOpen(const LLSD& key)
{
centerOnScreen();
}
void LLFloaterNewFeatureNotification::onCloseBtn()
{
closeFloater();
}
void LLFloaterNewFeatureNotification::centerOnScreen()
{
LLVector2 window_size = LLUI::getInstance()->getWindowSize();
centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
LLFloaterView* parent = dynamic_cast<LLFloaterView*>(getParent());
if (parent)
{
parent->bringToFront(this);
}
}

View File

@ -0,0 +1,49 @@
/**
* @file llfloaternewfeaturenotification.h
* @brief LLFloaterNewFeatureNotification class definition
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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_FLOATER_NEW_FEATURE_NOTOFICATION_H
#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H
#include "llfloater.h"
class LLFloaterNewFeatureNotification:
public LLFloater
{
friend class LLFloaterReg;
public:
BOOL postBuild() override;
void onOpen(const LLSD& key) override;
private:
LLFloaterNewFeatureNotification(const LLSD& key);
/*virtual*/ ~LLFloaterNewFeatureNotification();
void centerOnScreen();
void onCloseBtn();
};
#endif

View File

@ -164,34 +164,12 @@ void LLFloaterOpenObject::moveToInventory(bool wear, bool replace)
}
inventory_func_type func = boost::bind(LLFloaterOpenObject::callbackCreateInventoryCategory,_1,object_id,wear,replace);
LLUUID category_id = gInventory.createNewCategory(parent_category_id,
LLFolderType::FT_NONE,
name,
func);
//If we get a null category ID, we are using a capability in createNewCategory and we will
//handle the following in the callbackCreateInventoryCategory routine.
if ( category_id.notNull() )
{
LLCatAndWear* data = new LLCatAndWear;
data->mCatID = category_id;
data->mWear = wear;
data->mFolderResponded = false;
data->mReplace = replace;
// Copy and/or move the items into the newly created folder.
// Ignore any "you're going to break this item" messages.
BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
callbackMoveInventory,
(void*)data);
if (!success)
{
delete data;
data = NULL;
LLNotificationsUtil::add("OpenObjectCannotCopy");
}
}
// D567 copy thumbnail info
gInventory.createNewCategory(
parent_category_id,
LLFolderType::FT_NONE,
name,
func);
}
// static
@ -206,9 +184,14 @@ void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLUUID& category
// Copy and/or move the items into the newly created folder.
// Ignore any "you're going to break this item" messages.
BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
callbackMoveInventory,
(void*)wear_data);
BOOL success = move_inv_category_world_to_agent(object_id,
category_id,
TRUE,
[](S32 result, void* data, const LLMoveInv*)
{
callbackMoveInventory(result, data);
},
(void*)wear_data);
if (!success)
{
delete wear_data;

View File

@ -1,288 +0,0 @@
/**
* @file llfloateroutfitphotopreview.cpp
* @brief LLFloaterOutfitPhotoPreview class implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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 "llwindow.h"
#include "llfloateroutfitphotopreview.h"
#include "llagent.h"
#include "llappearancemgr.h"
#include "llbutton.h"
#include "llcombobox.h"
#include "llfilepicker.h"
#include "llfloaterreg.h"
#include "llimagetga.h"
#include "llimagepng.h"
#include "llinventory.h"
#include "llinventorymodel.h"
#include "llnotificationsutil.h"
#include "llresmgr.h"
#include "lltrans.h"
#include "lltextbox.h"
#include "lltextureview.h"
#include "llui.h"
#include "llviewerinventory.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
#include "lluictrlfactory.h"
#include "llviewerwindow.h"
#include "lllineeditor.h"
const S32 MAX_OUTFIT_PHOTO_WIDTH = 256;
const S32 MAX_OUTFIT_PHOTO_HEIGHT = 256;
const S32 CLIENT_RECT_VPAD = 4;
LLFloaterOutfitPhotoPreview::LLFloaterOutfitPhotoPreview(const LLSD& key)
: LLPreview(key),
mUpdateDimensions(TRUE),
mImage(NULL),
mOutfitID(LLUUID()),
mImageOldBoostLevel(LLGLTexture::BOOST_NONE),
mExceedLimits(FALSE)
{
updateImageID();
}
LLFloaterOutfitPhotoPreview::~LLFloaterOutfitPhotoPreview()
{
LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
if (mImage.notNull())
{
mImage->setBoostLevel(mImageOldBoostLevel);
mImage = NULL;
}
}
// virtual
BOOL LLFloaterOutfitPhotoPreview::postBuild()
{
getChild<LLButton>("ok_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onOkBtn, this));
getChild<LLButton>("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onCancelBtn, this));
return LLPreview::postBuild();
}
void LLFloaterOutfitPhotoPreview::draw()
{
updateDimensions();
LLPreview::draw();
if (!isMinimized())
{
LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
const LLRect& border = mClientRect;
LLRect interior = mClientRect;
interior.stretch( -PREVIEW_BORDER_WIDTH );
// ...border
gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
gl_rect_2d_checkerboard( interior );
if ( mImage.notNull() )
{
// Draw the texture
gGL.diffuseColor3f( 1.f, 1.f, 1.f );
gl_draw_scaled_image(interior.mLeft,
interior.mBottom,
interior.getWidth(),
interior.getHeight(),
mImage);
// Pump the texture priority
F32 pixel_area = (F32)(interior.getWidth() * interior.getHeight() );
mImage->addTextureStats( pixel_area );
S32 int_width = interior.getWidth();
S32 int_height = interior.getHeight();
mImage->setKnownDrawSize(int_width, int_height);
}
}
}
// virtual
void LLFloaterOutfitPhotoPreview::reshape(S32 width, S32 height, BOOL called_from_parent)
{
LLPreview::reshape(width, height, called_from_parent);
LLRect dim_rect(getChildView("dimensions")->getRect());
S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD;
LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0);
client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ;
S32 client_width = client_rect.getWidth();
S32 client_height = client_width;
if(client_height > client_rect.getHeight())
{
client_height = client_rect.getHeight();
client_width = client_height;
}
mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height);
}
void LLFloaterOutfitPhotoPreview::updateDimensions()
{
if (!mImage)
{
return;
}
if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0)
{
return;
}
if (mAssetStatus != PREVIEW_ASSET_LOADED)
{
mAssetStatus = PREVIEW_ASSET_LOADED;
mUpdateDimensions = TRUE;
}
getChild<LLUICtrl>("dimensions")->setTextArg("[WIDTH]", llformat("%d", mImage->getFullWidth()));
getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight()));
if ((mImage->getFullWidth() <= MAX_OUTFIT_PHOTO_WIDTH) && (mImage->getFullHeight() <= MAX_OUTFIT_PHOTO_HEIGHT))
{
getChild<LLButton>("ok_btn")->setEnabled(TRUE);
mExceedLimits = FALSE;
}
else
{
mExceedLimits = TRUE;
LLStringUtil::format_map_t args;
args["MAX_WIDTH"] = llformat("%d", MAX_OUTFIT_PHOTO_WIDTH);
args["MAX_HEIGHT"] = llformat("%d", MAX_OUTFIT_PHOTO_HEIGHT);
std::string label = getString("exceed_limits", args);
getChild<LLUICtrl>("notification")->setValue(label);
getChild<LLUICtrl>("notification")->setColor(LLColor4::yellow);
getChild<LLButton>("ok_btn")->setEnabled(FALSE);
}
if (mUpdateDimensions)
{
mUpdateDimensions = FALSE;
reshape(getRect().getWidth(), getRect().getHeight());
gFloaterView->adjustToFitScreen(this, FALSE);
}
}
void LLFloaterOutfitPhotoPreview::loadAsset()
{
if (mImage.notNull())
{
mImage->setBoostLevel(mImageOldBoostLevel);
}
mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
mImageOldBoostLevel = mImage->getBoostLevel();
mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
mImage->forceToSaveRawImage(0) ;
mAssetStatus = PREVIEW_ASSET_LOADING;
mUpdateDimensions = TRUE;
updateDimensions();
}
LLPreview::EAssetStatus LLFloaterOutfitPhotoPreview::getAssetStatus()
{
if (mImage.notNull() && (mImage->getFullWidth() * mImage->getFullHeight() > 0))
{
mAssetStatus = PREVIEW_ASSET_LOADED;
}
return mAssetStatus;
}
void LLFloaterOutfitPhotoPreview::updateImageID()
{
const LLViewerInventoryItem *item = static_cast<const LLViewerInventoryItem*>(getItem());
if(item)
{
mImageID = item->getAssetUUID();
}
else
{
mImageID = mItemUUID;
}
}
/* virtual */
void LLFloaterOutfitPhotoPreview::setObjectID(const LLUUID& object_id)
{
mObjectUUID = object_id;
const LLUUID old_image_id = mImageID;
updateImageID();
if (mImageID != old_image_id)
{
mAssetStatus = PREVIEW_ASSET_UNLOADED;
loadAsset();
}
refreshFromItem();
}
void LLFloaterOutfitPhotoPreview::setOutfitID(const LLUUID& outfit_id)
{
mOutfitID = outfit_id;
LLViewerInventoryCategory* outfit_folder = gInventory.getCategory(mOutfitID);
if(outfit_folder && !mExceedLimits)
{
getChild<LLUICtrl>("notification")->setValue( getString("photo_confirmation"));
getChild<LLUICtrl>("notification")->setTextArg("[OUTFIT]", outfit_folder->getName());
getChild<LLUICtrl>("notification")->setColor(LLColor4::white);
}
}
void LLFloaterOutfitPhotoPreview::onOkBtn()
{
if(mOutfitID.notNull() && getItem())
{
LLAppearanceMgr::instance().removeOutfitPhoto(mOutfitID);
LLPointer<LLInventoryCallback> cb = NULL;
link_inventory_object(mOutfitID, LLConstPointer<LLInventoryObject>(getItem()), cb);
}
closeFloater();
}
void LLFloaterOutfitPhotoPreview::onCancelBtn()
{
closeFloater();
}

View File

@ -1,77 +0,0 @@
/**
* @file llfloateroutfitphotopreview.h
* @brief LLFloaterOutfitPhotoPreview class definition
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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_LLFLOATEROUTFITPHOTOPREVIEW_H
#define LL_LLFLOATEROUTFITPHOTOPREVIEW_H
#include "llpreview.h"
#include "llbutton.h"
#include "llframetimer.h"
#include "llviewertexture.h"
class LLComboBox;
class LLImageRaw;
class LLFloaterOutfitPhotoPreview : public LLPreview
{
public:
LLFloaterOutfitPhotoPreview(const LLSD& key);
~LLFloaterOutfitPhotoPreview();
virtual void draw();
virtual void loadAsset();
virtual EAssetStatus getAssetStatus();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
/*virtual*/ void setObjectID(const LLUUID& object_id);
void setOutfitID(const LLUUID& outfit_id);
void onOkBtn();
void onCancelBtn();
protected:
void init();
/* virtual */ BOOL postBuild();
private:
void updateImageID(); // set what image is being uploaded.
void updateDimensions();
LLUUID mImageID;
LLUUID mOutfitID;
LLPointer<LLViewerFetchedTexture> mImage;
S32 mImageOldBoostLevel;
// This is stored off in a member variable, because the save-as
// button and drag and drop functionality need to know.
BOOL mUpdateDimensions;
BOOL mExceedLimits;
LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
};
#endif // LL_LLFLOATEROUTFITPHOTOPREVIEW_H

View File

@ -1,891 +0,0 @@
/**
* @file llfloaterproperties.cpp
* @brief A floater which shows an inventory item's properties.
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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 "llfloaterproperties.h"
#include <algorithm>
#include <functional>
#include "llcachename.h"
#include "llavatarnamecache.h"
#include "lldbstrings.h"
#include "llfloaterreg.h"
#include "llagent.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llavataractions.h"
#include "llinventorydefines.h"
#include "llinventoryobserver.h"
#include "llinventorymodel.h"
#include "lllineeditor.h"
//#include "llspinctrl.h"
#include "llradiogroup.h"
#include "llresmgr.h"
#include "roles_constants.h"
#include "llselectmgr.h"
#include "lltextbox.h"
#include "lltrans.h"
#include "lluiconstants.h"
#include "llviewerinventory.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llgroupactions.h"
#include "lluictrlfactory.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLPropertiesObserver
//
// helper class to watch the inventory.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ugh. This can't be a singleton because it needs to remove itself
// from the inventory observer list when destroyed, which could
// happen after gInventory has already been destroyed if a singleton.
// Instead, do our own ref counting and create / destroy it as needed
class LLPropertiesObserver : public LLInventoryObserver
{
public:
LLPropertiesObserver(LLFloaterProperties* floater)
: mFloater(floater)
{
gInventory.addObserver(this);
}
virtual ~LLPropertiesObserver()
{
gInventory.removeObserver(this);
}
virtual void changed(U32 mask);
private:
LLFloaterProperties* mFloater; // Not a handle because LLFloaterProperties is managing LLPropertiesObserver
};
void LLPropertiesObserver::changed(U32 mask)
{
// if there's a change we're interested in.
if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
{
mFloater->dirty();
}
}
///----------------------------------------------------------------------------
/// Class LLFloaterProperties
///----------------------------------------------------------------------------
// Default constructor
LLFloaterProperties::LLFloaterProperties(const LLUUID& item_id)
: LLFloater(mItemID),
mItemID(item_id),
mDirty(TRUE)
{
mPropertiesObserver = new LLPropertiesObserver(this);
}
// Destroys the object
LLFloaterProperties::~LLFloaterProperties()
{
delete mPropertiesObserver;
mPropertiesObserver = NULL;
}
// virtual
BOOL LLFloaterProperties::postBuild()
{
// build the UI
// item name & description
getChild<LLLineEditor>("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitName,this));
getChild<LLLineEditor>("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);
getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLFloaterProperties:: onCommitDescription, this));
// Creator information
getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickCreator,this));
// owner information
getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickOwner,this));
// acquired date
// owner permissions
// Permissions debug text
// group permissions
getChild<LLUICtrl>("CheckShareWithGroup")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
// everyone permissions
getChild<LLUICtrl>("CheckEveryoneCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
// next owner permissions
getChild<LLUICtrl>("CheckNextOwnerModify")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
getChild<LLUICtrl>("CheckNextOwnerCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
getChild<LLUICtrl>("CheckNextOwnerTransfer")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this));
// Mark for sale or not, and sale info
getChild<LLUICtrl>("CheckPurchase")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this));
getChild<LLUICtrl>("ComboBoxSaleType")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleType, this));
// "Price" label for edit
getChild<LLUICtrl>("Edit Cost")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this));
// The UI has been built, now fill in all the values
refresh();
return TRUE;
}
// virtual
void LLFloaterProperties::onOpen(const LLSD& key)
{
refresh();
}
void LLFloaterProperties::refresh()
{
LLInventoryItem* item = findItem();
if(item)
{
refreshFromItem(item);
}
else
{
//RN: it is possible that the container object is in the middle of an inventory refresh
// causing findItem() to fail, so just temporarily disable everything
mDirty = TRUE;
const char* enableNames[]={
"LabelItemName",
"LabelItemDesc",
"LabelCreatorName",
"BtnCreator",
"LabelOwnerName",
"BtnOwner",
"CheckOwnerModify",
"CheckOwnerCopy",
"CheckOwnerTransfer",
"CheckShareWithGroup",
"CheckEveryoneCopy",
"CheckNextOwnerModify",
"CheckNextOwnerCopy",
"CheckNextOwnerTransfer",
"CheckPurchase",
"ComboBoxSaleType",
"Edit Cost"
};
for(size_t t=0; t<LL_ARRAY_SIZE(enableNames); ++t)
{
getChildView(enableNames[t])->setEnabled(false);
}
const char* hideNames[]={
"BaseMaskDebug",
"OwnerMaskDebug",
"GroupMaskDebug",
"EveryoneMaskDebug",
"NextMaskDebug"
};
for(size_t t=0; t<LL_ARRAY_SIZE(hideNames); ++t)
{
getChildView(hideNames[t])->setVisible(false);
}
}
}
void LLFloaterProperties::draw()
{
if (mDirty)
{
// RN: clear dirty first because refresh can set dirty to TRUE
mDirty = FALSE;
refresh();
}
LLFloater::draw();
}
void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
{
////////////////////////
// PERMISSIONS LOOKUP //
////////////////////////
// do not enable the UI for incomplete items.
LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
BOOL is_complete = i->isFinished();
const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(i->getInventoryType());
const BOOL is_calling_card = (i->getInventoryType() == LLInventoryType::IT_CALLINGCARD);
const LLPermissions& perm = item->getPermissions();
const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,
GP_OBJECT_MANIPULATE);
const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,
GP_OBJECT_SET_SALE) &&
!cannot_restrict_permissions;
const BOOL is_link = i->getIsLinkType();
// You need permission to modify the object to modify an inventory
// item in it.
LLViewerObject* object = NULL;
if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID);
BOOL is_obj_modify = TRUE;
if(object)
{
is_obj_modify = object->permOwnerModify();
}
//////////////////////
// ITEM NAME & DESC //
//////////////////////
BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
GP_OBJECT_MANIPULATE)
&& is_obj_modify && is_complete;
getChildView("LabelItemNameTitle")->setEnabled(TRUE);
getChildView("LabelItemName")->setEnabled(is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards
getChild<LLUICtrl>("LabelItemName")->setValue(item->getName());
getChildView("LabelItemDescTitle")->setEnabled(TRUE);
getChildView("LabelItemDesc")->setEnabled(is_modifiable);
getChildView("IconLocked")->setVisible(!is_modifiable);
getChild<LLUICtrl>("LabelItemDesc")->setValue(item->getDescription());
//////////////////
// CREATOR NAME //
//////////////////
if(!gCacheName) return;
if(!gAgent.getRegion()) return;
if (item->getCreatorUUID().notNull())
{
LLAvatarName av_name;
LLAvatarNameCache::get(item->getCreatorUUID(), &av_name);
getChildView("BtnCreator")->setEnabled(TRUE);
getChildView("LabelCreatorTitle")->setEnabled(TRUE);
getChildView("LabelCreatorName")->setEnabled(TRUE);
getChild<LLUICtrl>("LabelCreatorName")->setValue(av_name.getUserName());
}
else
{
getChildView("BtnCreator")->setEnabled(FALSE);
getChildView("LabelCreatorTitle")->setEnabled(FALSE);
getChildView("LabelCreatorName")->setEnabled(FALSE);
getChild<LLUICtrl>("LabelCreatorName")->setValue(getString("unknown"));
}
////////////////
// OWNER NAME //
////////////////
if(perm.isOwned())
{
std::string name;
if (perm.isGroupOwned())
{
gCacheName->getGroupName(perm.getGroup(), name);
}
else
{
LLAvatarName av_name;
LLAvatarNameCache::get(perm.getOwner(), &av_name);
name = av_name.getUserName();
}
getChildView("BtnOwner")->setEnabled(TRUE);
getChildView("LabelOwnerTitle")->setEnabled(TRUE);
getChildView("LabelOwnerName")->setEnabled(TRUE);
getChild<LLUICtrl>("LabelOwnerName")->setValue(name);
}
else
{
getChildView("BtnOwner")->setEnabled(FALSE);
getChildView("LabelOwnerTitle")->setEnabled(FALSE);
getChildView("LabelOwnerName")->setEnabled(FALSE);
getChild<LLUICtrl>("LabelOwnerName")->setValue(getString("public"));
}
//////////////////
// ACQUIRE DATE //
//////////////////
time_t time_utc = item->getCreationDate();
if (0 == time_utc)
{
getChild<LLUICtrl>("LabelAcquiredDate")->setValue(getString("unknown"));
}
else
{
std::string timeStr = getString("acquiredDate");
LLSD substitution;
substitution["datetime"] = (S32) time_utc;
LLStringUtil::format (timeStr, substitution);
getChild<LLUICtrl>("LabelAcquiredDate")->setValue(timeStr);
}
///////////////////////
// OWNER PERMISSIONS //
///////////////////////
if(can_agent_manipulate)
{
getChild<LLUICtrl>("OwnerLabel")->setValue(getString("you_can"));
}
else
{
getChild<LLUICtrl>("OwnerLabel")->setValue(getString("owner_can"));
}
U32 base_mask = perm.getMaskBase();
U32 owner_mask = perm.getMaskOwner();
U32 group_mask = perm.getMaskGroup();
U32 everyone_mask = perm.getMaskEveryone();
U32 next_owner_mask = perm.getMaskNextOwner();
getChildView("OwnerLabel")->setEnabled(TRUE);
getChildView("CheckOwnerModify")->setEnabled(FALSE);
getChild<LLUICtrl>("CheckOwnerModify")->setValue(LLSD((BOOL)(owner_mask & PERM_MODIFY)));
getChildView("CheckOwnerCopy")->setEnabled(FALSE);
getChild<LLUICtrl>("CheckOwnerCopy")->setValue(LLSD((BOOL)(owner_mask & PERM_COPY)));
getChildView("CheckOwnerTransfer")->setEnabled(FALSE);
getChild<LLUICtrl>("CheckOwnerTransfer")->setValue(LLSD((BOOL)(owner_mask & PERM_TRANSFER)));
///////////////////////
// DEBUG PERMISSIONS //
///////////////////////
if( gSavedSettings.getBOOL("DebugPermissions") )
{
BOOL slam_perm = FALSE;
BOOL overwrite_group = FALSE;
BOOL overwrite_everyone = FALSE;
if (item->getType() == LLAssetType::AT_OBJECT)
{
U32 flags = item->getFlags();
slam_perm = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM;
overwrite_everyone = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
overwrite_group = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
}
std::string perm_string;
perm_string = "B: ";
perm_string += mask_to_string(base_mask);
getChild<LLUICtrl>("BaseMaskDebug")->setValue(perm_string);
getChildView("BaseMaskDebug")->setVisible(TRUE);
perm_string = "O: ";
perm_string += mask_to_string(owner_mask);
getChild<LLUICtrl>("OwnerMaskDebug")->setValue(perm_string);
getChildView("OwnerMaskDebug")->setVisible(TRUE);
perm_string = "G";
perm_string += overwrite_group ? "*: " : ": ";
perm_string += mask_to_string(group_mask);
getChild<LLUICtrl>("GroupMaskDebug")->setValue(perm_string);
getChildView("GroupMaskDebug")->setVisible(TRUE);
perm_string = "E";
perm_string += overwrite_everyone ? "*: " : ": ";
perm_string += mask_to_string(everyone_mask);
getChild<LLUICtrl>("EveryoneMaskDebug")->setValue(perm_string);
getChildView("EveryoneMaskDebug")->setVisible(TRUE);
perm_string = "N";
perm_string += slam_perm ? "*: " : ": ";
perm_string += mask_to_string(next_owner_mask);
getChild<LLUICtrl>("NextMaskDebug")->setValue(perm_string);
getChildView("NextMaskDebug")->setVisible(TRUE);
}
else
{
getChildView("BaseMaskDebug")->setVisible(FALSE);
getChildView("OwnerMaskDebug")->setVisible(FALSE);
getChildView("GroupMaskDebug")->setVisible(FALSE);
getChildView("EveryoneMaskDebug")->setVisible(FALSE);
getChildView("NextMaskDebug")->setVisible(FALSE);
}
/////////////
// SHARING //
/////////////
// Check for ability to change values.
if (is_link || cannot_restrict_permissions)
{
getChildView("CheckShareWithGroup")->setEnabled(FALSE);
getChildView("CheckEveryoneCopy")->setEnabled(FALSE);
}
else if (is_obj_modify && can_agent_manipulate)
{
getChildView("CheckShareWithGroup")->setEnabled(TRUE);
getChildView("CheckEveryoneCopy")->setEnabled((owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
}
else
{
getChildView("CheckShareWithGroup")->setEnabled(FALSE);
getChildView("CheckEveryoneCopy")->setEnabled(FALSE);
}
// Set values.
BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE;
BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE;
BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE;
if (is_group_copy && is_group_modify && is_group_move)
{
getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)TRUE));
LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
if(ctl)
{
ctl->setTentative(FALSE);
}
}
else if (!is_group_copy && !is_group_modify && !is_group_move)
{
getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)FALSE));
LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
if(ctl)
{
ctl->setTentative(FALSE);
}
}
else
{
LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
if(ctl)
{
ctl->setTentative(TRUE);
ctl->set(TRUE);
}
}
getChild<LLUICtrl>("CheckEveryoneCopy")->setValue(LLSD((BOOL)(everyone_mask & PERM_COPY)));
///////////////
// SALE INFO //
///////////////
const LLSaleInfo& sale_info = item->getSaleInfo();
BOOL is_for_sale = sale_info.isForSale();
LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType");
LLUICtrl* edit_cost = getChild<LLUICtrl>("Edit Cost");
// Check for ability to change values.
if (is_obj_modify && can_agent_sell
&& gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
{
getChildView("CheckPurchase")->setEnabled(is_complete);
getChildView("NextOwnerLabel")->setEnabled(TRUE);
getChildView("CheckNextOwnerModify")->setEnabled((base_mask & PERM_MODIFY) && !cannot_restrict_permissions);
getChildView("CheckNextOwnerCopy")->setEnabled((base_mask & PERM_COPY) && !cannot_restrict_permissions);
getChildView("CheckNextOwnerTransfer")->setEnabled((next_owner_mask & PERM_COPY) && !cannot_restrict_permissions);
combo_sale_type->setEnabled(is_complete && is_for_sale);
edit_cost->setEnabled(is_complete && is_for_sale);
}
else
{
getChildView("CheckPurchase")->setEnabled(FALSE);
getChildView("NextOwnerLabel")->setEnabled(FALSE);
getChildView("CheckNextOwnerModify")->setEnabled(FALSE);
getChildView("CheckNextOwnerCopy")->setEnabled(FALSE);
getChildView("CheckNextOwnerTransfer")->setEnabled(FALSE);
combo_sale_type->setEnabled(FALSE);
edit_cost->setEnabled(FALSE);
}
// Set values.
getChild<LLUICtrl>("CheckPurchase")->setValue(is_for_sale);
getChild<LLUICtrl>("CheckNextOwnerModify")->setValue(LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
getChild<LLUICtrl>("CheckNextOwnerCopy")->setValue(LLSD(BOOL(next_owner_mask & PERM_COPY)));
getChild<LLUICtrl>("CheckNextOwnerTransfer")->setValue(LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
if (is_for_sale)
{
S32 numerical_price;
numerical_price = sale_info.getSalePrice();
edit_cost->setValue(llformat("%d",numerical_price));
combo_sale_type->setValue(sale_info.getSaleType());
}
else
{
edit_cost->setValue(llformat("%d",0));
combo_sale_type->setValue(LLSaleInfo::FS_COPY);
}
}
void LLFloaterProperties::onClickCreator()
{
LLInventoryItem* item = findItem();
if(!item) return;
if(!item->getCreatorUUID().isNull())
{
LLAvatarActions::showProfile(item->getCreatorUUID());
}
}
// static
void LLFloaterProperties::onClickOwner()
{
LLInventoryItem* item = findItem();
if(!item) return;
if(item->getPermissions().isGroupOwned())
{
LLGroupActions::show(item->getPermissions().getGroup());
}
else
{
LLAvatarActions::showProfile(item->getPermissions().getOwner());
}
}
// static
void LLFloaterProperties::onCommitName()
{
//LL_INFOS() << "LLFloaterProperties::onCommitName()" << LL_ENDL;
LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
if(!item)
{
return;
}
LLLineEditor* labelItemName = getChild<LLLineEditor>("LabelItemName");
if(labelItemName&&
(item->getName() != labelItemName->getText()) &&
(gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) )
{
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->rename(labelItemName->getText());
if(mObjectID.isNull())
{
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
else
{
LLViewerObject* object = gObjectList.findObject(mObjectID);
if(object)
{
object->updateInventory(
new_item,
TASK_INVENTORY_ITEM_KEY,
false);
}
}
}
}
void LLFloaterProperties::onCommitDescription()
{
//LL_INFOS() << "LLFloaterProperties::onCommitDescription()" << LL_ENDL;
LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
if(!item) return;
LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc");
if(!labelItemDesc)
{
return;
}
if((item->getDescription() != labelItemDesc->getText()) &&
(gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))
{
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setDescription(labelItemDesc->getText());
if(mObjectID.isNull())
{
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
else
{
LLViewerObject* object = gObjectList.findObject(mObjectID);
if(object)
{
object->updateInventory(
new_item,
TASK_INVENTORY_ITEM_KEY,
false);
}
}
}
}
// static
void LLFloaterProperties::onCommitPermissions()
{
//LL_INFOS() << "LLFloaterProperties::onCommitPermissions()" << LL_ENDL;
LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
if(!item) return;
LLPermissions perm(item->getPermissions());
LLCheckBoxCtrl* CheckShareWithGroup = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
if(CheckShareWithGroup)
{
perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(),
CheckShareWithGroup->get(),
PERM_MODIFY | PERM_MOVE | PERM_COPY);
}
LLCheckBoxCtrl* CheckEveryoneCopy = getChild<LLCheckBoxCtrl>("CheckEveryoneCopy");
if(CheckEveryoneCopy)
{
perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(),
CheckEveryoneCopy->get(), PERM_COPY);
}
LLCheckBoxCtrl* CheckNextOwnerModify = getChild<LLCheckBoxCtrl>("CheckNextOwnerModify");
if(CheckNextOwnerModify)
{
perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
CheckNextOwnerModify->get(), PERM_MODIFY);
}
LLCheckBoxCtrl* CheckNextOwnerCopy = getChild<LLCheckBoxCtrl>("CheckNextOwnerCopy");
if(CheckNextOwnerCopy)
{
perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
CheckNextOwnerCopy->get(), PERM_COPY);
}
LLCheckBoxCtrl* CheckNextOwnerTransfer = getChild<LLCheckBoxCtrl>("CheckNextOwnerTransfer");
if(CheckNextOwnerTransfer)
{
perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
CheckNextOwnerTransfer->get(), PERM_TRANSFER);
}
if(perm != item->getPermissions()
&& item->isFinished())
{
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setPermissions(perm);
U32 flags = new_item->getFlags();
// If next owner permissions have changed (and this is an object)
// then set the slam permissions flag so that they are applied on rez.
if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner())
&& (item->getType() == LLAssetType::AT_OBJECT))
{
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM;
}
// If everyone permissions have changed (and this is an object)
// then set the overwrite everyone permissions flag so they
// are applied on rez.
if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone())
&& (item->getType() == LLAssetType::AT_OBJECT))
{
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
}
// If group permissions have changed (and this is an object)
// then set the overwrite group permissions flag so they
// are applied on rez.
if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup())
&& (item->getType() == LLAssetType::AT_OBJECT))
{
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
}
new_item->setFlags(flags);
if(mObjectID.isNull())
{
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
else
{
LLViewerObject* object = gObjectList.findObject(mObjectID);
if(object)
{
object->updateInventory(
new_item,
TASK_INVENTORY_ITEM_KEY,
false);
}
}
}
else
{
// need to make sure we don't just follow the click
refresh();
}
}
// static
void LLFloaterProperties::onCommitSaleInfo()
{
//LL_INFOS() << "LLFloaterProperties::onCommitSaleInfo()" << LL_ENDL;
updateSaleInfo();
}
// static
void LLFloaterProperties::onCommitSaleType()
{
//LL_INFOS() << "LLFloaterProperties::onCommitSaleType()" << LL_ENDL;
updateSaleInfo();
}
void LLFloaterProperties::updateSaleInfo()
{
LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
if(!item) return;
LLSaleInfo sale_info(item->getSaleInfo());
if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
{
getChild<LLUICtrl>("CheckPurchase")->setValue(LLSD((BOOL)FALSE));
}
if((BOOL)getChild<LLUICtrl>("CheckPurchase")->getValue())
{
// turn on sale info
LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType");
if (combo_sale_type)
{
sale_type = static_cast<LLSaleInfo::EForSale>(combo_sale_type->getValue().asInteger());
}
if (sale_type == LLSaleInfo::FS_COPY
&& !gAgent.allowOperation(PERM_COPY, item->getPermissions(),
GP_OBJECT_SET_SALE))
{
sale_type = LLSaleInfo::FS_ORIGINAL;
}
S32 price = -1;
price = getChild<LLUICtrl>("Edit Cost")->getValue().asInteger();;
// Invalid data - turn off the sale
if (price < 0)
{
sale_type = LLSaleInfo::FS_NOT;
price = 0;
}
sale_info.setSaleType(sale_type);
sale_info.setSalePrice(price);
}
else
{
sale_info.setSaleType(LLSaleInfo::FS_NOT);
}
if(sale_info != item->getSaleInfo()
&& item->isFinished())
{
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
// Force an update on the sale price at rez
if (item->getType() == LLAssetType::AT_OBJECT)
{
U32 flags = new_item->getFlags();
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_SALE;
new_item->setFlags(flags);
}
new_item->setSaleInfo(sale_info);
if(mObjectID.isNull())
{
// This is in the agent's inventory.
new_item->updateServer(FALSE);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
}
else
{
// This is in an object's contents.
LLViewerObject* object = gObjectList.findObject(mObjectID);
if(object)
{
object->updateInventory(
new_item,
TASK_INVENTORY_ITEM_KEY,
false);
}
}
}
else
{
// need to make sure we don't just follow the click
refresh();
}
}
LLInventoryItem* LLFloaterProperties::findItem() const
{
LLInventoryItem* item = NULL;
if(mObjectID.isNull())
{
// it is in agent inventory
item = gInventory.getItem(mItemID);
}
else
{
LLViewerObject* object = gObjectList.findObject(mObjectID);
if(object)
{
item = (LLInventoryItem*)object->getInventoryObject(mItemID);
}
}
return item;
}
//static
void LLFloaterProperties::dirtyAll()
{
LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("properties");
for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
iter != inst_list.end(); ++iter)
{
LLFloaterProperties* floater = dynamic_cast<LLFloaterProperties*>(*iter);
llassert(floater); // else cast failed - wrong type D:
if (floater)
{
floater->dirty();
}
}
}
///----------------------------------------------------------------------------
/// LLMultiProperties
///----------------------------------------------------------------------------
LLMultiProperties::LLMultiProperties()
: LLMultiFloater(LLSD())
{
// start with a small rect in the top-left corner ; will get resized
LLRect rect;
rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 20, 20);
setRect(rect);
LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup("properties");
if (last_floater)
{
stackWith(*last_floater);
}
setTitle(LLTrans::getString("MultiPropertiesTitle"));
buildTabContainer();
}
///----------------------------------------------------------------------------
/// Local function definitions
///----------------------------------------------------------------------------

View File

@ -1,98 +0,0 @@
/**
* @file llfloaterproperties.h
* @brief A floater which shows an inventory item's properties.
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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_LLFLOATERPROPERTIES_H
#define LL_LLFLOATERPROPERTIES_H
#include <map>
#include "llmultifloater.h"
#include "lliconctrl.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLFloaterProperties
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLButton;
class LLCheckBoxCtrl;
class LLInventoryItem;
class LLLineEditor;
class LLRadioGroup;
class LLTextBox;
class LLPropertiesObserver;
class LLFloaterProperties : public LLFloater
{
public:
LLFloaterProperties(const LLUUID& item_id);
/*virtual*/ ~LLFloaterProperties();
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
void setObjectID(const LLUUID& object_id) { mObjectID = object_id; }
void dirty() { mDirty = TRUE; }
void refresh();
static void dirtyAll();
protected:
// ui callbacks
void onClickCreator();
void onClickOwner();
void onCommitName();
void onCommitDescription();
void onCommitPermissions();
void onCommitSaleInfo();
void onCommitSaleType();
void updateSaleInfo();
LLInventoryItem* findItem() const;
void refreshFromItem(LLInventoryItem* item);
virtual void draw();
protected:
// The item id of the inventory item in question.
LLUUID mItemID;
// mObjectID will have a value if it is associated with a task in
// the world, and will be == LLUUID::null if it's in the agent
// inventory.
LLUUID mObjectID;
BOOL mDirty;
LLPropertiesObserver* mPropertiesObserver;
};
class LLMultiProperties : public LLMultiFloater
{
public:
LLMultiProperties();
};
#endif // LL_LLFLOATERPROPERTIES_H

View File

@ -90,6 +90,29 @@ void LLFloaterSidePanelContainer::closeFloater(bool app_quitting)
}
}
LLFloater* LLFloaterSidePanelContainer::getTopmostInventoryFloater()
{
LLFloater* topmost_floater = NULL;
S32 z_min = S32_MAX;
LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
{
LLFloater* inventory_floater = (*iter);
if (inventory_floater && inventory_floater->getVisible())
{
S32 z_order = gFloaterView->getZOrder(inventory_floater);
if (z_order < z_min)
{
z_min = z_order;
topmost_floater = inventory_floater;
}
}
}
return topmost_floater;
}
LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_name, const LLSD& params)
{
LLView* view = findChildView(panel_name, true);

View File

@ -57,6 +57,8 @@ public:
LLPanel* openChildPanel(const std::string& panel_name, const LLSD& params);
static LLFloater* getTopmostInventoryFloater();
static void showPanel(const std::string& floater_name, const LLSD& key);
static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key);

View File

@ -1,333 +0,0 @@
/**
* @file llfloatersimpleoutfitsnapshot.cpp
* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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 "llfloatersimpleoutfitsnapshot.h"
#include "llfloaterreg.h"
#include "llimagefiltersmanager.h"
#include "llstatusbar.h" // can_afford_transaction()
#include "llnotificationsutil.h"
#include "llagentbenefits.h"
#include "llviewercontrol.h"
LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView = NULL;
const S32 OUTFIT_SNAPSHOT_WIDTH = 256;
const S32 OUTFIT_SNAPSHOT_HEIGHT = 256;
static LLDefaultChildRegistry::Register<LLSimpleOutfitSnapshotFloaterView> r("simple_snapshot_outfit_floater_view");
///----------------------------------------------------------------------------
/// Class LLFloaterSimpleOutfitSnapshot::Impl
///----------------------------------------------------------------------------
LLSnapshotModel::ESnapshotFormat LLFloaterSimpleOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater)
{
return LLSnapshotModel::SNAPSHOT_FORMAT_PNG;
}
LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater)
{
return LLSnapshotModel::SNAPSHOT_TYPE_COLOR;
}
void LLFloaterSimpleOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
updateResolution(floater);
if (previewp)
{
previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE);
previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG);
previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR);
}
}
std::string LLFloaterSimpleOutfitSnapshot::Impl::getSnapshotPanelPrefix()
{
return "panel_outfit_snapshot_";
}
void LLFloaterSimpleOutfitSnapshot::Impl::updateResolution(void* data)
{
LLFloaterSimpleOutfitSnapshot *view = (LLFloaterSimpleOutfitSnapshot *)data;
if (!view)
{
llassert(view);
return;
}
S32 width = OUTFIT_SNAPSHOT_WIDTH;
S32 height = OUTFIT_SNAPSHOT_HEIGHT;
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
S32 original_width = 0, original_height = 0;
previewp->getSize(original_width, original_height);
if (gSavedSettings.getBOOL("RenderHUDInSnapshot"))
{ //clamp snapshot resolution to window size when showing UI HUD in snapshot
width = llmin(width, gViewerWindow->getWindowWidthRaw());
height = llmin(height, gViewerWindow->getWindowHeightRaw());
}
llassert(width > 0 && height > 0);
previewp->setSize(width, height);
if (original_width != width || original_height != height)
{
// hide old preview as the aspect ratio could be wrong
checkAutoSnapshot(previewp, FALSE);
previewp->updateSnapshot(TRUE);
}
}
}
void LLFloaterSimpleOutfitSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg)
{
switch (status)
{
case STATUS_READY:
mFloater->setCtrlsEnabled(true);
break;
case STATUS_WORKING:
mFloater->setCtrlsEnabled(false);
break;
case STATUS_FINISHED:
mFloater->setCtrlsEnabled(true);
break;
}
mStatus = status;
}
///----------------------------------------------------------------re------------
/// Class LLFloaterSimpleOutfitSnapshot
///----------------------------------------------------------------------------
LLFloaterSimpleOutfitSnapshot::LLFloaterSimpleOutfitSnapshot(const LLSD& key)
: LLFloaterSnapshotBase(key),
mOutfitGallery(NULL)
{
impl = new Impl(this);
}
LLFloaterSimpleOutfitSnapshot::~LLFloaterSimpleOutfitSnapshot()
{
}
BOOL LLFloaterSimpleOutfitSnapshot::postBuild()
{
getChild<LLUICtrl>("save_btn")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()));
childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this);
childSetAction("save_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onSend, this));
childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onCancel, this));
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
// create preview window
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
p.rect(full_screen_rect);
LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
LLView* parent_view = gSnapshotFloaterView->getParent();
parent_view->removeChild(gSnapshotFloaterView);
// make sure preview is below snapshot floater
parent_view->addChild(previewp);
parent_view->addChild(gSnapshotFloaterView);
//move snapshot floater to special purpose snapshotfloaterview
gFloaterView->removeChild(this);
gSnapshotFloaterView->addChild(this);
impl->mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
impl->updateControls(this);
impl->setAdvanced(true);
impl->setSkipReshaping(true);
previewp->mKeepAspectRatio = FALSE;
previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
previewp->setAllowRenderUI(false);
return TRUE;
}
const S32 PREVIEW_OFFSET_X = 12;
const S32 PREVIEW_OFFSET_Y = 70;
void LLFloaterSimpleOutfitSnapshot::draw()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock()))
{
// don't render snapshot window in snapshot, even if "show ui" is turned on
return;
}
LLFloater::draw();
if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible())
{
if(previewp->getThumbnailImage())
{
bool working = impl->getStatus() == ImplBase::STATUS_WORKING;
const LLRect& thumbnail_rect = getThumbnailPlaceholderRect();
const S32 thumbnail_w = previewp->getThumbnailWidth();
const S32 thumbnail_h = previewp->getThumbnailHeight();
S32 offset_x = PREVIEW_OFFSET_X;
S32 offset_y = PREVIEW_OFFSET_Y;
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
LLColor4 color = working ? LLColor4::grey4 : LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y,
thumbnail_w, thumbnail_h,
previewp->getThumbnailImage(), color % alpha);
#if LL_DARWIN
std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "OutfitSnapshotMacMask" : "OutfitSnapshotMacMask2";
#else
std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "FloaterFocusBackgroundColor" : "DkGray";
#endif
previewp->drawPreviewRect(offset_x, offset_y, LLUIColorTable::instance().getColor(alpha_color));
gGL.pushUIMatrix();
LLUI::translate((F32) thumbnail_rect.mLeft, (F32) thumbnail_rect.mBottom);
mThumbnailPlaceholder->draw();
gGL.popUIMatrix();
}
}
impl->updateLayout(this);
}
void LLFloaterSimpleOutfitSnapshot::onOpen(const LLSD& key)
{
LLSnapshotLivePreview* preview = getPreviewView();
if (preview)
{
preview->updateSnapshot(TRUE);
}
focusFirstItem(FALSE);
gSnapshotFloaterView->setEnabled(TRUE);
gSnapshotFloaterView->setVisible(TRUE);
gSnapshotFloaterView->adjustToFitScreen(this, FALSE);
impl->updateControls(this);
impl->setStatus(ImplBase::STATUS_READY);
}
void LLFloaterSimpleOutfitSnapshot::onCancel()
{
closeFloater();
}
void LLFloaterSimpleOutfitSnapshot::onSend()
{
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
if (can_afford_transaction(expected_upload_cost))
{
saveTexture();
postSave();
}
else
{
LLSD args;
args["COST"] = llformat("%d", expected_upload_cost);
LLNotificationsUtil::add("ErrorPhotoCannotAfford", args);
inventorySaveFailed();
}
}
void LLFloaterSimpleOutfitSnapshot::postSave()
{
impl->setStatus(ImplBase::STATUS_WORKING);
}
// static
void LLFloaterSimpleOutfitSnapshot::update()
{
LLFloaterSimpleOutfitSnapshot* inst = findInstance();
if (inst != NULL)
{
inst->impl->updateLivePreview();
}
}
// static
LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::findInstance()
{
return LLFloaterReg::findTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot");
}
// static
LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::getInstance()
{
return LLFloaterReg::getTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot");
}
void LLFloaterSimpleOutfitSnapshot::saveTexture()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (!previewp)
{
llassert(previewp != NULL);
return;
}
if (mOutfitGallery)
{
mOutfitGallery->onBeforeOutfitSnapshotSave();
}
previewp->saveTexture(TRUE, getOutfitID().asString());
if (mOutfitGallery)
{
mOutfitGallery->onAfterOutfitSnapshotSave();
}
closeFloater();
}
///----------------------------------------------------------------------------
/// Class LLSimpleOutfitSnapshotFloaterView
///----------------------------------------------------------------------------
LLSimpleOutfitSnapshotFloaterView::LLSimpleOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p)
{
}
LLSimpleOutfitSnapshotFloaterView::~LLSimpleOutfitSnapshotFloaterView()
{
}

View File

@ -0,0 +1,499 @@
/**
* @file llfloatersimplesnapshot.cpp
* @brief Snapshot preview window for saving as a thumbnail
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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 "llfloatersimplesnapshot.h"
#include "llfloaterreg.h"
#include "llimagefiltersmanager.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
#include "llstatusbar.h" // can_afford_transaction()
#include "llnotificationsutil.h"
#include "llagent.h"
#include "llagentbenefits.h"
#include "llviewercontrol.h"
#include "llviewertexturelist.h"
LLSimpleSnapshotFloaterView* gSimpleSnapshotFloaterView = NULL;
const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX = 256;
const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN = 64;
// Thumbnail posting coro
static const std::string THUMBNAIL_UPLOAD_CAP = "InventoryThumbnailUpload";
void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, LLSD first_data)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_coro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpHeaders::ptr_t httpHeaders;
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
httpOpts->setFollowRedirects(true);
LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
if (!status)
{
// todo: notification?
LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
return;
}
if (!result.has("uploader"))
{
// todo: notification?
LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
return;
}
std::string uploader_cap = result["uploader"].asString();
if (uploader_cap.empty())
{
LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
return;
}
// Upload the image
LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
S64 length;
{
llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
if (!instream.is_open())
{
LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
return;
}
length = instream.tellg();
}
uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
uploaderhttpOpts->setFollowRedirects(true);
result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
LL_DEBUGS("Thumbnail") << result << LL_ENDL;
if (!status)
{
LL_WARNS("Thumbnail") << "Failed to upload image " << status.toString() << LL_ENDL;
return;
}
if (result["state"].asString() != "complete")
{
if (result.has("message"))
{
LL_WARNS("Thumbnail") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
}
else
{
LL_WARNS("Thumbnail") << "Failed to upload image " << result << LL_ENDL;
}
return;
}
if (first_data.has("category_id"))
{
LLUUID cat_id = first_data["category_id"].asUUID();
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
if (cat)
{
cat->setThumbnailUUID(result["new_asset"].asUUID());
}
gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat_id);
}
if (first_data.has("item_id"))
{
LLUUID item_id = first_data["item_id"].asUUID();
LLViewerInventoryItem* item = gInventory.getItem(item_id);
if (item)
{
item->setThumbnailUUID(result["new_asset"].asUUID());
}
// Are we supposed to get BulkUpdateInventory?
gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
}
}
///----------------------------------------------------------------------------
/// Class LLFloaterSimpleSnapshot::Impl
///----------------------------------------------------------------------------
LLSnapshotModel::ESnapshotFormat LLFloaterSimpleSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater)
{
return LLSnapshotModel::SNAPSHOT_FORMAT_PNG;
}
LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater)
{
return LLSnapshotModel::SNAPSHOT_TYPE_COLOR;
}
void LLFloaterSimpleSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater)
{
LLSnapshotLivePreview* previewp = getPreviewView();
updateResolution(floater);
if (previewp)
{
previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE);
previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG);
previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR);
}
}
std::string LLFloaterSimpleSnapshot::Impl::getSnapshotPanelPrefix()
{
return "panel_outfit_snapshot_";
}
void LLFloaterSimpleSnapshot::Impl::updateResolution(void* data)
{
LLFloaterSimpleSnapshot *view = (LLFloaterSimpleSnapshot *)data;
if (!view)
{
llassert(view);
return;
}
S32 width = THUMBNAIL_SNAPSHOT_DIM_MAX;
S32 height = THUMBNAIL_SNAPSHOT_DIM_MAX;
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp)
{
S32 original_width = 0, original_height = 0;
previewp->getSize(original_width, original_height);
if (gSavedSettings.getBOOL("RenderHUDInSnapshot"))
{ //clamp snapshot resolution to window size when showing UI HUD in snapshot
width = llmin(width, gViewerWindow->getWindowWidthRaw());
height = llmin(height, gViewerWindow->getWindowHeightRaw());
}
llassert(width > 0 && height > 0);
previewp->setSize(width, height);
if (original_width != width || original_height != height)
{
// hide old preview as the aspect ratio could be wrong
checkAutoSnapshot(previewp, FALSE);
previewp->updateSnapshot(TRUE);
}
}
}
void LLFloaterSimpleSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg)
{
switch (status)
{
case STATUS_READY:
mFloater->setCtrlsEnabled(true);
break;
case STATUS_WORKING:
mFloater->setCtrlsEnabled(false);
break;
case STATUS_FINISHED:
mFloater->setCtrlsEnabled(true);
break;
}
mStatus = status;
}
///----------------------------------------------------------------re------------
/// Class LLFloaterSimpleSnapshot
///----------------------------------------------------------------------------
LLFloaterSimpleSnapshot::LLFloaterSimpleSnapshot(const LLSD& key)
: LLFloaterSnapshotBase(key)
, mOwner(NULL)
, mContextConeOpacity(0.f)
{
impl = new Impl(this);
}
LLFloaterSimpleSnapshot::~LLFloaterSimpleSnapshot()
{
}
BOOL LLFloaterSimpleSnapshot::postBuild()
{
childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this);
childSetAction("save_btn", boost::bind(&LLFloaterSimpleSnapshot::onSend, this));
childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleSnapshot::onCancel, this));
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
// create preview window
LLRect full_screen_rect = getRootView()->getRect();
LLSnapshotLivePreview::Params p;
p.rect(full_screen_rect);
LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
// Do not move LLFloaterSimpleSnapshot floater into gSnapshotFloaterView
// since it can be a dependednt floater and does not draw UI
impl->mPreviewHandle = previewp->getHandle();
previewp->setContainer(this);
impl->updateControls(this);
impl->setAdvanced(true);
impl->setSkipReshaping(true);
previewp->mKeepAspectRatio = FALSE;
previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect());
previewp->setAllowRenderUI(false);
previewp->setThumbnailSubsampled(TRUE);
return TRUE;
}
const S32 PREVIEW_OFFSET_Y = 70;
void LLFloaterSimpleSnapshot::draw()
{
if (mOwner)
{
static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
drawConeToOwner(mContextConeOpacity, max_opacity, mOwner);
}
LLSnapshotLivePreview* previewp = getPreviewView();
if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock()))
{
// don't render snapshot window in snapshot, even if "show ui" is turned on
return;
}
LLFloater::draw();
if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible())
{
if(previewp->getThumbnailImage())
{
bool working = impl->getStatus() == ImplBase::STATUS_WORKING;
const S32 thumbnail_w = previewp->getThumbnailWidth();
const S32 thumbnail_h = previewp->getThumbnailHeight();
LLRect local_rect = getLocalRect();
S32 offset_x = (local_rect.getWidth() - thumbnail_w) / 2;
S32 offset_y = PREVIEW_OFFSET_Y;
gGL.matrixMode(LLRender::MM_MODELVIEW);
// Apply floater transparency to the texture unless the floater is focused.
F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
LLColor4 color = working ? LLColor4::grey4 : LLColor4::white;
gl_draw_scaled_image(offset_x, offset_y,
thumbnail_w, thumbnail_h,
previewp->getThumbnailImage(), color % alpha);
}
}
impl->updateLayout(this);
}
void LLFloaterSimpleSnapshot::onOpen(const LLSD& key)
{
LLSnapshotLivePreview* preview = getPreviewView();
if (preview)
{
preview->updateSnapshot(TRUE);
}
focusFirstItem(FALSE);
gSnapshotFloaterView->setEnabled(TRUE);
gSnapshotFloaterView->setVisible(TRUE);
gSnapshotFloaterView->adjustToFitScreen(this, FALSE);
impl->updateControls(this);
impl->setStatus(ImplBase::STATUS_READY);
mInventoryId = key["item_id"].asUUID();
mTaskId = key["task_id"].asUUID();
}
void LLFloaterSimpleSnapshot::onCancel()
{
closeFloater();
}
void LLFloaterSimpleSnapshot::onSend()
{
LLSnapshotLivePreview* previewp = getPreviewView();
std::string temp_file = gDirUtilp->getTempFilename();
if (previewp->createUploadFile(temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN))
{
uploadImageUploadFile(temp_file, mInventoryId, mTaskId);
closeFloater();
}
else
{
LLSD notif_args;
notif_args["REASON"] = LLImage::getLastError().c_str();
LLNotificationsUtil::add("CannotUploadTexture", notif_args);
}
}
void LLFloaterSimpleSnapshot::postSave()
{
impl->setStatus(ImplBase::STATUS_WORKING);
}
// static
void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id)
{
// generate a temp texture file for coroutine
std::string temp_file = gDirUtilp->getTempFilename();
U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path));
if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN, true))
{
LLSD notif_args;
notif_args["REASON"] = LLImage::getLastError().c_str();
LLNotificationsUtil::add("CannotUploadTexture", notif_args);
LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
return;
}
uploadImageUploadFile(temp_file, inventory_id, task_id);
}
// static
void LLFloaterSimpleSnapshot::uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id)
{
std::string temp_file = gDirUtilp->getTempFilename();
if (!LLViewerTextureList::createUploadFile(raw_image, temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN))
{
LLSD notif_args;
notif_args["REASON"] = LLImage::getLastError().c_str();
LLNotificationsUtil::add("CannotUploadTexture", notif_args);
LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
return;
}
uploadImageUploadFile(temp_file, inventory_id, task_id);
}
// static
void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id)
{
LLSD data;
if (task_id.notNull())
{
data["item_id"] = inventory_id;
data["task_id"] = task_id;
}
else if (gInventory.getCategory(inventory_id))
{
data["category_id"] = inventory_id;
}
else
{
data["item_id"] = inventory_id;
}
std::string cap_url = gAgent.getRegionCapability(THUMBNAIL_UPLOAD_CAP);
if (cap_url.empty())
{
LLSD args;
args["CAPABILITY"] = THUMBNAIL_UPLOAD_CAP;
LLNotificationsUtil::add("RegionCapabilityRequestError", args);
LL_WARNS("Thumbnail") << "Failed to upload profile image for item " << inventory_id << " " << task_id << ", no cap found" << LL_ENDL;
return;
}
LLCoros::instance().launch("postAgentUserImageCoro",
boost::bind(post_thumbnail_image_coro, cap_url, temp_file, data));
}
void LLFloaterSimpleSnapshot::update()
{
// initializes snapshots when needed
LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("simple_snapshot");
for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
iter != inst_list.end(); ++iter)
{
LLFloaterSimpleSnapshot* floater = dynamic_cast<LLFloaterSimpleSnapshot*>(*iter);
if (floater)
{
floater->impl->updateLivePreview();
}
}
}
// static
LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::findInstance(const LLSD &key)
{
return LLFloaterReg::findTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key);
}
// static
LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::getInstance(const LLSD &key)
{
return LLFloaterReg::getTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key);
}
void LLFloaterSimpleSnapshot::saveTexture()
{
LLSnapshotLivePreview* previewp = getPreviewView();
if (!previewp)
{
llassert(previewp != NULL);
return;
}
previewp->saveTexture(TRUE, getInventoryId().asString());
closeFloater();
}
///----------------------------------------------------------------------------
/// Class LLSimpleOutfitSnapshotFloaterView
///----------------------------------------------------------------------------
LLSimpleSnapshotFloaterView::LLSimpleSnapshotFloaterView(const Params& p) : LLFloaterView(p)
{
}
LLSimpleSnapshotFloaterView::~LLSimpleSnapshotFloaterView()
{
}

View File

@ -1,6 +1,6 @@
/**
* @file llfloatersimpleoutfitsnapshot.h
* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery
* @file llfloatersimplesnapshot.h
* @brief Snapshot preview window for saving as a thumbnail
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
@ -24,26 +24,25 @@
* $/LicenseInfo$
*/
#ifndef LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
#define LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
#ifndef LL_LLFLOATERSIMPLESNAPSHOT_H
#define LL_LLFLOATERSIMPLESNAPSHOT_H
#include "llfloater.h"
#include "llfloatersnapshot.h"
#include "lloutfitgallery.h"
#include "llsnapshotlivepreview.h"
///----------------------------------------------------------------------------
/// Class LLFloaterSimpleOutfitSnapshot
/// Class LLFloaterSimpleSnapshot
///----------------------------------------------------------------------------
class LLFloaterSimpleOutfitSnapshot : public LLFloaterSnapshotBase
class LLFloaterSimpleSnapshot : public LLFloaterSnapshotBase
{
LOG_CLASS(LLFloaterSimpleOutfitSnapshot);
LOG_CLASS(LLFloaterSimpleSnapshot);
public:
LLFloaterSimpleOutfitSnapshot(const LLSD& key);
~LLFloaterSimpleOutfitSnapshot();
LLFloaterSimpleSnapshot(const LLSD& key);
~LLFloaterSimpleSnapshot();
BOOL postBuild();
void onOpen(const LLSD& key);
@ -51,36 +50,48 @@ public:
static void update();
static LLFloaterSimpleOutfitSnapshot* getInstance();
static LLFloaterSimpleOutfitSnapshot* findInstance();
static LLFloaterSimpleSnapshot* getInstance(const LLSD &key);
static LLFloaterSimpleSnapshot* findInstance(const LLSD &key);
void saveTexture();
const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); }
void setOutfitID(LLUUID id) { mOutfitID = id; }
LLUUID getOutfitID() { return mOutfitID; }
void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; }
void setInventoryId(const LLUUID &inventory_id) { mInventoryId = inventory_id; }
LLUUID getInventoryId() { return mInventoryId; }
void setTaskId(const LLUUID &task_id) { mTaskId = task_id; }
void setOwner(LLView *owner_view) { mOwner = owner_view; }
void postSave();
static void uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id);
static void uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id);
class Impl;
friend class Impl;
static const S32 THUMBNAIL_SNAPSHOT_DIM_MAX;
static const S32 THUMBNAIL_SNAPSHOT_DIM_MIN;
private:
void onSend();
void onCancel();
LLUUID mOutfitID;
LLOutfitGallery* mOutfitGallery;
// uploads upload-ready file
static void uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id);
LLUUID mInventoryId;
LLUUID mTaskId;
LLView* mOwner;
F32 mContextConeOpacity;
};
///----------------------------------------------------------------------------
/// Class LLFloaterSimpleOutfitSnapshot::Impl
/// Class LLFloaterSimpleSnapshot::Impl
///----------------------------------------------------------------------------
class LLFloaterSimpleOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase
class LLFloaterSimpleSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase
{
LOG_CLASS(LLFloaterSimpleOutfitSnapshot::Impl);
LOG_CLASS(LLFloaterSimpleSnapshot::Impl);
public:
Impl(LLFloaterSnapshotBase* floater)
: LLFloaterSnapshotBase::ImplBase(floater)
@ -108,7 +119,7 @@ private:
/// Class LLSimpleOutfitSnapshotFloaterView
///----------------------------------------------------------------------------
class LLSimpleOutfitSnapshotFloaterView : public LLFloaterView
class LLSimpleSnapshotFloaterView : public LLFloaterView
{
public:
struct Params
@ -117,13 +128,13 @@ public:
};
protected:
LLSimpleOutfitSnapshotFloaterView(const Params& p);
LLSimpleSnapshotFloaterView(const Params& p);
friend class LLUICtrlFactory;
public:
virtual ~LLSimpleOutfitSnapshotFloaterView();
virtual ~LLSimpleSnapshotFloaterView();
};
extern LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView;
extern LLSimpleSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView;
#endif // LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H
#endif // LL_LLFLOATERSIMPLESNAPSHOT_H

View File

@ -39,12 +39,14 @@ class LLFolderViewModelItemInventory
public:
LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model);
virtual const LLUUID& getUUID() const = 0;
virtual const LLUUID& getThumbnailUUID() const = 0;
virtual time_t getCreationDate() const = 0; // UTC seconds
virtual void setCreationDate(time_t creation_date_utc) = 0;
virtual PermissionMask getPermissionMask() const = 0;
virtual LLFolderType::EType getPreferredType() const = 0;
virtual void showProperties(void) = 0;
virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual.
virtual bool isItemInOutfits() const { return false; }
virtual BOOL isAgentInventory() const { return FALSE; }
virtual BOOL isUpToDate() const = 0;
virtual void addChild(LLFolderViewModelItem* child);

View File

@ -478,14 +478,24 @@ void LLFriendCardsManager::ensureFriendsFolderExists()
LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL;
}
friends_folder_ID = gInventory.createNewCategory(calling_cards_folder_ID,
LLFolderType::FT_CALLINGCARD, get_friend_folder_name());
gInventory.createNewCategory(friends_folder_ID,
LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name());
// Now when we have all needed folders we can sync their contents with buddies list.
syncFriendsFolder();
gInventory.createNewCategory(
calling_cards_folder_ID,
LLFolderType::FT_CALLINGCARD,
get_friend_folder_name(),
[](const LLUUID &new_category_id)
{
gInventory.createNewCategory(
new_category_id,
LLFolderType::FT_CALLINGCARD,
get_friend_all_subfolder_name(),
[](const LLUUID &new_category_id)
{
// Now when we have all needed folders we can sync their contents with buddies list.
LLFriendCardsManager::getInstance()->syncFriendsFolder();
}
);
}
);
}
}
@ -510,11 +520,16 @@ void LLFriendCardsManager::ensureFriendsAllFolderExists()
LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL;
}
friends_all_folder_ID = gInventory.createNewCategory(friends_folder_ID,
LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name());
// Now when we have all needed folders we can sync their contents with buddies list.
syncFriendsFolder();
gInventory.createNewCategory(
friends_folder_ID,
LLFolderType::FT_CALLINGCARD,
get_friend_all_subfolder_name(),
[](const LLUUID &new_cat_id)
{
// Now when we have all needed folders we can sync their contents with buddies list.
LLFriendCardsManager::getInstance()->syncFriendsFolder();
}
);
}
}

View File

@ -662,9 +662,7 @@ void LLInspectObject::onClickOpen()
void LLInspectObject::onClickMoreInfo()
{
LLSD key;
key["task"] = "task";
LLFloaterSidePanelContainer::showPanel("inventory", key);
LLFloaterReg::showInstance("task_properties");
closeFloater();
}

View File

@ -0,0 +1,249 @@
/**
* @file llinspecttexture.cpp
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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 "llinspect.h"
#include "llinspecttexture.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "lltexturectrl.h"
#include "lltrans.h"
#include "llviewertexturelist.h"
// ============================================================================
// Helper functions
//
LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p)
{
const LLSD& sdTooltip = p.create_params;
if (sdTooltip.has("thumbnail_id") && sdTooltip["thumbnail_id"].asUUID().notNull())
{
// go straight for thumbnail regardless of type
// TODO: make a tooltip factory?
return LLUICtrlFactory::create<LLTextureToolTip>(p);
}
LLInventoryType::EType eInvType = (sdTooltip.has("inv_type")) ? (LLInventoryType::EType)sdTooltip["inv_type"].asInteger() : LLInventoryType::IT_NONE;
switch (eInvType)
{
case LLInventoryType::IT_CATEGORY:
{
if (sdTooltip.has("item_id"))
{
const LLUUID idCategory = sdTooltip["item_id"].asUUID();
LLViewerInventoryCategory* cat = gInventory.getCategory(idCategory);
if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
{
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
// Not LLIsOfAssetType, because we allow links
LLIsTextureType f;
gInventory.getDirectDescendentsOf(idCategory, cats, items, f);
// Exactly one texture found => show the texture tooltip
if (1 == items.size())
{
LLViewerInventoryItem* item = items.front();
if (item && item->getIsLinkType())
{
item = item->getLinkedItem();
}
if (item)
{
// Note: LLFloaterChangeItemThumbnail will attempt to write this
// into folder's thumbnail id when opened
p.create_params.getValue()["thumbnail_id"] = item->getAssetUUID();
return LLUICtrlFactory::create<LLTextureToolTip>(p);
}
}
}
}
// No or more than one texture found => show default tooltip
return LLUICtrlFactory::create<LLToolTip>(p);
}
default:
return LLUICtrlFactory::create<LLToolTip>(p);
}
}
// ============================================================================
// LLTexturePreviewView helper class
//
class LLTexturePreviewView : public LLView
{
public:
LLTexturePreviewView(const LLView::Params& p);
~LLTexturePreviewView();
public:
void draw() override;
public:
void setImageFromAssetId(const LLUUID& idAsset);
void setImageFromItemId(const LLUUID& idItem);
protected:
LLPointer<LLViewerFetchedTexture> m_Image;
S32 mImageBoostLevel = LLGLTexture::BOOST_NONE;
std::string mLoadingText;
};
LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p)
: LLView(p)
{
mLoadingText = LLTrans::getString("texture_loading");
}
LLTexturePreviewView::~LLTexturePreviewView()
{
if (m_Image)
{
m_Image->setBoostLevel(mImageBoostLevel);
m_Image = nullptr;
}
}
void LLTexturePreviewView::draw()
{
LLView::draw();
if (m_Image)
{
LLRect rctClient = getLocalRect();
if (4 == m_Image->getComponents())
{
const LLColor4 color(.098f, .098f, .098f);
gl_rect_2d(rctClient, color, TRUE);
}
gl_draw_scaled_image(rctClient.mLeft, rctClient.mBottom, rctClient.getWidth(), rctClient.getHeight(), m_Image);
bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0);
if (isLoading)
LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, llfloor(rctClient.mLeft + 3), llfloor(rctClient.mTop - 25), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight()));
}
}
void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset)
{
m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
if (m_Image)
{
mImageBoostLevel = m_Image->getBoostLevel();
m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
m_Image->forceToSaveRawImage(0);
if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) )
{
if (m_Image->isInFastCacheList())
{
m_Image->loadFromFastCache();
}
gTextureList.forceImmediateUpdate(m_Image);
}
}
}
void LLTexturePreviewView::setImageFromItemId(const LLUUID& idItem)
{
const LLViewerInventoryItem* pItem = gInventory.getItem(idItem);
setImageFromAssetId( (pItem) ? pItem->getAssetUUID() : LLUUID::null );
}
// ============================================================================
// LLTextureToolTip class
//
LLTextureToolTip::LLTextureToolTip(const LLToolTip::Params& p)
: LLToolTip(p)
, mPreviewView(nullptr)
, mPreviewSize(256)
{
mMaxWidth = llmax(mMaxWidth, mPreviewSize);
// Currently has to share params with LLToolTip, override values
setBackgroundColor(LLColor4::black);
setTransparentColor(LLColor4::black);
setBorderVisible(true);
}
LLTextureToolTip::~LLTextureToolTip()
{
}
void LLTextureToolTip::initFromParams(const LLToolTip::Params& p)
{
LLToolTip::initFromParams(p);
// Create and add the preview control
LLView::Params p_preview;
p_preview.name = "texture_preview";
LLRect rctPreview;
rctPreview.setOriginAndSize(mPadding, mTextBox->getRect().mTop, mPreviewSize, mPreviewSize);
p_preview.rect = rctPreview;
mPreviewView = LLUICtrlFactory::create<LLTexturePreviewView>(p_preview);
addChild(mPreviewView);
// Parse the control params
const LLSD& sdTextureParams = p.create_params;
if (sdTextureParams.has("thumbnail_id"))
{
mPreviewView->setImageFromAssetId(sdTextureParams["thumbnail_id"].asUUID());
}
else if (sdTextureParams.has("item_id"))
{
mPreviewView->setImageFromItemId(sdTextureParams["item_id"].asUUID());
}
// Currently has to share params with LLToolTip, override values manually
// Todo: provide from own params instead, may be like object inspector does it
LLViewBorder::Params border_params;
border_params.border_thickness(LLPANEL_BORDER_WIDTH);
border_params.highlight_light_color(LLColor4::white);
border_params.highlight_dark_color(LLColor4::white);
border_params.shadow_light_color(LLColor4::white);
border_params.shadow_dark_color(LLColor4::white);
addBorder(border_params);
setBorderVisible(true);
setBackgroundColor(LLColor4::black);
setBackgroundVisible(true);
setBackgroundOpaque(true);
setBackgroundImage(nullptr);
setTransparentImage(nullptr);
mTextBox->setColor(LLColor4::white);
snapToChildren();
}
// ============================================================================

View File

@ -0,0 +1,49 @@
/**
* @file llinspecttexture.h
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#pragma once
#include "lltooltip.h"
class LLTexturePreviewView;
namespace LLInspectTextureUtil
{
LLToolTip* createInventoryToolTip(LLToolTip::Params p);
}
class LLTextureToolTip : public LLToolTip
{
public:
LLTextureToolTip(const LLToolTip::Params& p);
~LLTextureToolTip();
public:
void initFromParams(const LLToolTip::Params& p) override;
protected:
LLTexturePreviewView* mPreviewView;
S32 mPreviewSize;
};

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,6 @@
#define LL_LLINVENTORYBRIDGE_H
#include "llcallingcard.h"
#include "llfloaterproperties.h"
#include "llfolderviewmodel.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
@ -47,9 +46,11 @@ class LLMenuGL;
class LLCallingCardObserver;
class LLViewerJointAttachment;
class LLFolderView;
struct LLMoveInv;
typedef std::vector<std::string> menuentry_vec_t;
typedef std::pair<LLUUID, LLUUID> two_uuids_t;
typedef std::list<two_uuids_t> two_uuids_list_t;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLInvFVBridge
//
@ -84,6 +85,7 @@ public:
// LLInvFVBridge functionality
//--------------------------------------------------------------------
virtual const LLUUID& getUUID() const { return mUUID; }
virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null; }
virtual void clearDisplayName() { mDisplayName.clear(); }
virtual void restoreItem() {}
virtual void restoreToWorld() {}
@ -107,6 +109,7 @@ public:
virtual std::string getLabelSuffix() const { return LLStringUtil::null; }
virtual void openItem() {}
virtual void closeItem() {}
virtual void navigateToFolder(bool new_window = false, bool change_mode = false);
virtual void showProperties();
virtual BOOL isItemRenameable() const { return TRUE; }
virtual BOOL isMultiPreviewAllowed() { return TRUE; }
@ -114,6 +117,7 @@ public:
virtual BOOL isItemRemovable() const;
virtual BOOL isItemMovable() const;
virtual BOOL isItemInTrash() const;
virtual bool isItemInOutfits() const;
virtual BOOL isLink() const;
virtual BOOL isLibraryItem() const;
//virtual BOOL removeItem() = 0;
@ -251,6 +255,7 @@ public:
virtual LLUIImagePtr getIconOverlay() const;
LLViewerInventoryItem* getItem() const;
virtual const LLUUID& getThumbnailUUID() const;
protected:
BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response);
@ -275,8 +280,8 @@ public:
mShowDescendantsCount(false)
{}
BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE);
BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE);
BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL);
BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL);
void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item);
void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category);
@ -296,6 +301,7 @@ public:
static LLUIImagePtr getIcon(LLFolderType::EType preferred_type);
virtual std::string getLabelSuffix() const;
virtual LLFontGL::StyleFlags getLabelStyle() const;
virtual const LLUUID& getThumbnailUUID() const;
void setShowDescendantsCount(bool show_count) {mShowDescendantsCount = show_count;}
@ -335,6 +341,7 @@ public:
protected:
void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items);
void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items);
void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items);
//--------------------------------------------------------------------
// Menu callbacks
@ -360,9 +367,9 @@ protected:
void copyOutfitToClipboard();
void determineFolderType();
void dropToFavorites(LLInventoryItem* inv_item);
void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
void dropToMyOutfits(LLInventoryCategory* inv_cat);
void dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb = NULL);
void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb = NULL);
void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options
@ -372,7 +379,7 @@ public:
static void staticFolderOptionsMenu();
protected:
void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id);
void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb);
void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);
void perform_pasteFromClipboard();
void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level);
@ -741,7 +748,7 @@ void rez_attachment(LLViewerInventoryItem* item,
BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
const LLUUID& category_id,
BOOL drop,
void (*callback)(S32, void*) = NULL,
std::function<void(S32, void*, const LLMoveInv *)> callback = NULL,
void* user_data = NULL,
LLInventoryFilter* filter = NULL);
@ -767,4 +774,16 @@ public:
bool canWearSelected(const uuid_vec_t& item_ids) const;
};
struct LLMoveInv
{
LLUUID mObjectID;
LLUUID mCategoryID;
two_uuids_list_t mMoveList;
std::function<void(S32, void*, const LLMoveInv*)> mCallback;
void* mUserData;
};
void warn_move_inventory(LLViewerObject* object, boost::shared_ptr<LLMoveInv> move_inv);
bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr<LLMoveInv>);
#endif // LL_LLINVENTORYBRIDGE_H

View File

@ -63,6 +63,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p)
mFilterTypes(p.types),
mFilterUUID(p.uuid),
mFilterLinks(p.links),
mFilterThumbnails(p.thumbnails),
mSearchVisibility(p.search_visibility)
{
}
@ -81,7 +82,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p)
mCurrentGeneration(0),
mFirstRequiredGeneration(0),
mFirstSuccessGeneration(0),
mSearchType(SEARCHTYPE_NAME)
mSearchType(SEARCHTYPE_NAME),
mSingleFolderMode(false)
{
// copy mFilterOps into mDefaultFilterOps
markDefault();
@ -157,6 +159,8 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item)
passed = passed && checkAgainstCreator(listener);
passed = passed && checkAgainstSearchVisibility(listener);
passed = passed && checkAgainstFilterThumbnails(listener->getUUID());
return passed;
}
@ -194,17 +198,23 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
// when applying a filter, matching folders get their contents downloaded first
// but make sure we are not interfering with pre-download
if (isNotDefault()
&& LLStartUp::getStartupState() > STATE_WEARABLES_WAIT)
&& LLStartUp::getStartupState() > STATE_WEARABLES_WAIT
&& !LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress())
{
LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
if (!cat || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
if ((!cat && folder_id.notNull()) || (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
{
// At the moment background fetch only cares about VERSION_UNKNOWN,
// so do not check isCategoryComplete that compares descendant count
LLInventoryModelBackgroundFetch::instance().start(folder_id);
LLInventoryModelBackgroundFetch::instance().start(folder_id, false);
}
}
if (!checkAgainstFilterThumbnails(folder_id))
{
return false;
}
// Marketplace folder filtering
const U32 filterTypes = mFilterOps.mFilterTypes;
const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE |
@ -565,6 +575,19 @@ bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInven
return TRUE;
}
bool LLInventoryFilter::checkAgainstFilterThumbnails(const LLUUID& object_id) const
{
const LLInventoryObject *object = gInventory.getObject(object_id);
if (!object) return true;
const bool is_thumbnail = object->getThumbnailUUID().notNull();
if (is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS))
return false;
if (!is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS))
return false;
return true;
}
bool LLInventoryFilter::checkAgainstCreator(const LLFolderViewModelItemInventory* listener) const
{
if (!listener) return TRUE;
@ -595,6 +618,9 @@ bool LLInventoryFilter::checkAgainstSearchVisibility(const LLFolderViewModelItem
if (is_link && ((mFilterOps.mSearchVisibility & VISIBILITY_LINKS) == 0))
return FALSE;
if (listener->isItemInOutfits() && ((mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS) == 0))
return FALSE;
if (listener->isItemInTrash() && ((mFilterOps.mSearchVisibility & VISIBILITY_TRASH) == 0))
return FALSE;
@ -733,6 +759,32 @@ void LLInventoryFilter::setFilterSettingsTypes(U64 types)
mFilterOps.mFilterTypes |= FILTERTYPE_SETTINGS;
}
void LLInventoryFilter::setFilterThumbnails(U64 filter_thumbnails)
{
if (mFilterOps.mFilterThumbnails != filter_thumbnails)
{
if (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS
&& filter_thumbnails == FILTER_ONLY_THUMBNAILS)
{
setModified(FILTER_RESTART);
}
else if (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS
&& filter_thumbnails == FILTER_EXCLUDE_THUMBNAILS)
{
setModified(FILTER_RESTART);
}
else if (mFilterOps.mFilterThumbnails == FILTER_INCLUDE_THUMBNAILS)
{
setModified(FILTER_MORE_RESTRICTIVE);
}
else
{
setModified(FILTER_LESS_RESTRICTIVE);
}
}
mFilterOps.mFilterThumbnails = filter_thumbnails;
}
void LLInventoryFilter::setFilterEmptySystemFolders()
{
mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS;
@ -791,6 +843,24 @@ void LLInventoryFilter::toggleSearchVisibilityLinks()
}
}
void LLInventoryFilter::toggleSearchVisibilityOutfits()
{
bool hide_outfits = mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS;
if (hide_outfits)
{
mFilterOps.mSearchVisibility &= ~VISIBILITY_OUTFITS;
}
else
{
mFilterOps.mSearchVisibility |= VISIBILITY_OUTFITS;
}
if (hasFilterString())
{
setModified(hide_outfits ? FILTER_MORE_RESTRICTIVE : FILTER_LESS_RESTRICTIVE);
}
}
void LLInventoryFilter::toggleSearchVisibilityTrash()
{
bool hide_trash = mFilterOps.mSearchVisibility & VISIBILITY_TRASH;
@ -1503,6 +1573,11 @@ U64 LLInventoryFilter::getSearchVisibilityTypes() const
return mFilterOps.mSearchVisibility;
}
U64 LLInventoryFilter::getFilterThumbnails() const
{
return mFilterOps.mFilterThumbnails;
}
bool LLInventoryFilter::hasFilterString() const
{
return mFilterSubString.size() > 0;
@ -1580,9 +1655,9 @@ void LLInventoryFilter::setDefaultEmptyLookupMessage(const std::string& message)
mDefaultEmptyLookupMessage = message;
}
std::string LLInventoryFilter::getEmptyLookupMessage() const
std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const
{
if (isDefault() && !mDefaultEmptyLookupMessage.empty())
if ((isDefault() || is_empty_folder) && !mDefaultEmptyLookupMessage.empty())
{
return LLTrans::getString(mDefaultEmptyLookupMessage);
}
@ -1605,7 +1680,7 @@ bool LLInventoryFilter::areDateLimitsSet()
bool LLInventoryFilter::showAllResults() const
{
return hasFilterString();
return hasFilterString() && !mSingleFolderMode;
}

View File

@ -75,6 +75,13 @@ public:
FILTERLINK_ONLY_LINKS // only show links
};
enum EFilterThumbnail
{
FILTER_INCLUDE_THUMBNAILS,
FILTER_EXCLUDE_THUMBNAILS,
FILTER_ONLY_THUMBNAILS
};
enum ESortOrderType
{
SO_NAME = 0, // Sort inventory by name
@ -104,7 +111,8 @@ public:
VISIBILITY_NONE = 0,
VISIBILITY_TRASH = 0x1 << 0,
VISIBILITY_LIBRARY = 0x1 << 1,
VISIBILITY_LINKS = 0x1 << 2
VISIBILITY_LINKS = 0x1 << 2,
VISIBILITY_OUTFITS = 0x1 << 3
};
struct FilterOps
@ -139,12 +147,14 @@ public:
Optional<EFolderShow> show_folder_state;
Optional<PermissionMask> permissions;
Optional<EFilterCreatorType> creator_type;
Optional<EFilterThumbnail> thumbnails;
Params()
: types("filter_types", FILTERTYPE_OBJECT),
object_types("object_types", 0xffffFFFFffffFFFFULL),
wearable_types("wearable_types", 0xffffFFFFffffFFFFULL),
settings_types("settings_types", 0xffffFFFFffffFFFFULL),
thumbnails("thumbnails", FILTER_INCLUDE_THUMBNAILS),
category_types("category_types", 0xffffFFFFffffFFFFULL),
links("links", FILTERLINK_INCLUDE_LINKS),
search_visibility("search_visibility", 0xFFFFFFFF),
@ -165,6 +175,7 @@ public:
U64 mFilterObjectTypes, // For _OBJECT
mFilterWearableTypes,
mFilterSettingsTypes, // for _SETTINGS
mFilterThumbnails,
mFilterLinks,
mFilterCategoryTypes; // For _CATEGORY
LLUUID mFilterUUID; // for UUID
@ -207,6 +218,7 @@ public:
U64 getFilterWearableTypes() const;
U64 getFilterSettingsTypes() const;
U64 getSearchVisibilityTypes() const;
U64 getFilterThumbnails() const;
bool isFilterObjectTypesWith(LLInventoryType::EType t) const;
void setFilterObjectTypes(U64 types);
@ -221,6 +233,7 @@ public:
void setFilterMarketplaceUnassociatedFolders();
void setFilterMarketplaceListingFolders(bool select_only_listing_folders);
void setFilterNoMarketplaceFolder();
void setFilterThumbnails(U64 filter_thumbnails);
void updateFilterTypes(U64 types, U64& current_types);
void setSearchType(ESearchType type);
ESearchType getSearchType() { return mSearchType; }
@ -228,6 +241,7 @@ public:
void toggleSearchVisibilityLinks();
void toggleSearchVisibilityTrash();
void toggleSearchVisibilityOutfits();
void toggleSearchVisibilityLibrary();
void setSearchVisibilityTypes(U32 types);
void setSearchVisibilityTypes(const Params& params);
@ -237,6 +251,8 @@ public:
const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; }
bool hasFilterString() const;
void setSingleFolderMode(bool is_single_folder) { mSingleFolderMode = is_single_folder; }
void setFilterPermissions(PermissionMask perms);
PermissionMask getFilterPermissions() const;
@ -277,7 +293,7 @@ public:
void setEmptyLookupMessage(const std::string& message);
void setDefaultEmptyLookupMessage(const std::string& message);
std::string getEmptyLookupMessage() const;
std::string getEmptyLookupMessage(bool is_empty_folder = false) const;
// +-------------------------------------------------------------------+
// + Status
@ -321,6 +337,8 @@ public:
LLInventoryFilter& operator =(const LLInventoryFilter& other);
bool checkAgainstFilterThumbnails(const LLUUID& object_id) const;
private:
bool areDateLimitsSet();
bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const;
@ -359,6 +377,8 @@ private:
std::vector<std::string> mFilterTokens;
std::string mExactToken;
bool mSingleFolderMode;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,7 @@ void update_all_marketplace_count();
void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false);
void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id, bool move_no_copy_items, inventory_func_type callback);
void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items);
@ -91,13 +92,11 @@ std::string make_info(const LLInventoryObject* object);
// Generates a string containing the path name and id of the object specified by id.
std::string make_inventory_info(const LLUUID& id);
typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_callback_t;
bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false);
bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false);
bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false);
bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false);
bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1, bool notify_observers = true);
S32 depth_nesting_in_marketplace(LLUUID cur_uuid);
LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth);
S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false);
@ -108,10 +107,65 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected
bool is_only_cats_selected(const uuid_vec_t& selected_uuids);
bool is_only_items_selected(const uuid_vec_t& selected_uuids);
bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
bool can_move_to_landmarks(LLInventoryItem* inv_item);
bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit);
std::string get_localized_folder_name(LLUUID cat_uuid);
void new_folder_window(const LLUUID& folder_id);
void ungroup_folder_items(const LLUUID& folder_id);
std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id);
std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id);
std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id);
bool can_share_item(const LLUUID& item_id);
/** Miscellaneous global functions
** **
*******************************************************************************/
class LLMarketplaceValidator: public LLSingleton<LLMarketplaceValidator>
{
LLSINGLETON(LLMarketplaceValidator);
~LLMarketplaceValidator();
LOG_CLASS(LLMarketplaceValidator);
public:
typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_msg_callback_t;
typedef boost::function<void(bool result)> validation_done_callback_t;
void validateMarketplaceListings(
const LLUUID &category_id,
validation_done_callback_t cb_done = NULL,
validation_msg_callback_t cb_msg = NULL,
bool fix_hierarchy = true,
S32 depth = -1);
private:
void start();
class ValidationRequest
{
public:
ValidationRequest(
LLUUID category_id,
validation_done_callback_t cb_done,
validation_msg_callback_t cb_msg,
bool fix_hierarchy,
S32 depth);
LLUUID mCategoryId;
validation_done_callback_t mCbDone;
validation_msg_callback_t mCbMsg;
bool mFixHierarchy;
S32 mDepth;
};
bool mValidationInProgress;
S32 mPendingCallbacks;
bool mPendingResult;
// todo: might be a good idea to memorize requests by id and
// filter out ones that got multiple validation requests
std::queue<ValidationRequest> mValidationQueue;
};
/********************************************************************************
** **
** INVENTORY COLLECTOR FUNCTIONS
@ -327,6 +381,20 @@ public:
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLFindBrokenLinks
//
// Collects broken links
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLFindBrokenLinks : public LLInventoryCollectFunctor
{
public:
LLFindBrokenLinks() {}
virtual ~LLFindBrokenLinks() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item);
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLFindByMask
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -425,6 +493,15 @@ private:
LLWearableType::EType mWearableType;
};
class LLIsTextureType : public LLInventoryCollectFunctor
{
public:
LLIsTextureType() {}
virtual ~LLIsTextureType() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item);
};
/** Filter out wearables-links */
class LLFindActualWearablesOfType : public LLFindWearablesOfType
{
@ -484,7 +561,7 @@ struct LLInventoryAction
static void saveMultipleTextures(const std::vector<std::string>& filenames, std::set<LLFolderViewItem*> selected_items, LLInventoryModel* model);
static const int sConfirmOnDeleteItemsNumber;
static bool sDeleteConfirmationDisplayed;
private:
static void buildMarketplaceFolders(LLFolderView* root);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,419 @@
/**
* @file llinventorygallery.h
* @brief LLInventoryGallery class definition
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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_LLINVENTORYGALLERY_H
#define LL_LLINVENTORYGALLERY_H
#include "llgesturemgr.h"
#include "lllistcontextmenu.h"
#include "llpanel.h"
#include "llinventoryfilter.h"
#include "llinventoryobserver.h"
#include "llinventorymodel.h"
class LLInventoryCategoriesObserver;
class LLInventoryGalleryItem;
class LLScrollContainer;
class LLTextBox;
class LLThumbnailsObserver;
class LLGalleryGestureObserver;
class LLInventoryGalleryContextMenu;
typedef boost::function<void()> callback_t;
class LLInventoryGallery : public LLPanel, public LLEditMenuHandler
{
public:
typedef boost::signals2::signal<void(const LLUUID&)> selection_change_signal_t;
typedef boost::function<void(const LLUUID&)> selection_change_callback_t;
typedef std::deque<LLUUID> selection_deque;
struct Params
: public LLInitParam::Block<Params, LLPanel::Params>
{
Optional<S32> row_panel_height;
Optional<S32> row_panel_width_factor;
Optional<S32> gallery_width_factor;
Optional<S32> vertical_gap;
Optional<S32> horizontal_gap;
Optional<S32> item_width;
Optional<S32> item_height;
Optional<S32> item_horizontal_gap;
Optional<S32> items_in_row;
Params();
};
static const LLInventoryGallery::Params& getDefaultParams();
LLInventoryGallery(const LLInventoryGallery::Params& params = getDefaultParams());
~LLInventoryGallery();
BOOL postBuild() override;
void initGallery();
void draw() override;
void onVisibilityChange(BOOL new_visibility) override;
BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type,
void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override;
void startDrag();
BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override;
BOOL handleKeyHere(KEY key, MASK mask) override;
void moveUp(MASK mask);
void moveDown(MASK mask);
void moveLeft(MASK mask);
void moveRight(MASK mask);
void toggleSelectionRange(S32 start_idx, S32 end_idx);
void toggleSelectionRangeFromLast(const LLUUID target);
void onFocusLost() override;
void onFocusReceived() override;
void setFilterSubString(const std::string& string);
std::string getFilterSubString() { return mFilterSubString; }
LLInventoryFilter& getFilter() const { return *mFilter; }
bool checkAgainstFilterType(const LLUUID& object_id);
void getCurrentCategories(uuid_vec_t& vcur);
bool updateAddedItem(LLUUID item_id); // returns true if added item is visible
void updateRemovedItem(LLUUID item_id);
void updateChangedItemName(LLUUID item_id, std::string name);
void updateItemThumbnail(LLUUID item_id);
void updateWornItem(LLUUID item_id, bool is_worn);
void updateMessageVisibility();
void setRootFolder(const LLUUID cat_id);
void updateRootFolder();
LLUUID getRootFolder() { return mFolderID; }
bool isRootDirty() { return mRootDirty; }
boost::signals2::connection setRootChangedCallback(callback_t cb);
void onForwardFolder();
void onBackwardFolder();
void clearNavigationHistory();
bool isBackwardAvailable();
bool isForwardAvailable();
void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; }
void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; }
std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; }
std::list<LLUUID> getNavForwardList() { return mForwardFolders; }
LLUUID getOutfitImageID(LLUUID outfit_id);
void refreshList(const LLUUID& category_id);
void onCOFChanged();
void onGesturesChanged();
void computeDifference(const LLInventoryModel::cat_array_t vcats, const LLInventoryModel::item_array_t vitems, uuid_vec_t& vadded, uuid_vec_t& vremoved);
void deselectItem(const LLUUID& category_id);
void clearSelection();
void changeItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
void addItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
bool toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection = false);
void scrollToShowItem(const LLUUID& item_id);
void signalSelectionItemID(const LLUUID& category_id);
boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb);
LLUUID getFirstSelectedItemID();
void setSearchType(LLInventoryFilter::ESearchType type);
LLInventoryFilter::ESearchType getSearchType() { return mSearchType; }
bool areViewsInitialized();
bool hasDescendents(const LLUUID& cat_id);
bool hasVisibleItems();
void handleModifiedFilter();
LLScrollContainer* getScrollableContainer() { return mScrollPanel; }
LLInventoryGalleryItem* getFirstSelectedItem();
// Copy & paste (LLEditMenuHandler)
void copy() override;
BOOL canCopy() const override;
void cut() override;
BOOL canCut() const override;
void paste() override;
BOOL canPaste() const override;
// Copy & paste & delete
static void onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids);
void deleteSelection();
bool canDeleteSelection();
void pasteAsLink();
void setSortOrder(U32 order, bool update = false);
U32 getSortOrder() { return mSortOrder; };
void claimEditHandler();
void resetEditHandler();
static bool isItemCopyable(const LLUUID & item_id);
BOOL baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, EDragAndDropType cargo_type,
void* cargo_data, EAcceptance* accept, std::string& tooltip_msg);
void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id);
protected:
void paste(const LLUUID& dest,
std::vector<LLUUID>& objects,
bool is_cut_mode,
const LLUUID& marketplacelistings_id);
void pasteAsLink(const LLUUID& dest,
std::vector<LLUUID>& objects,
const LLUUID& current_outfit_id,
const LLUUID& marketplacelistings_id,
const LLUUID& my_outifts_id);
bool applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring);
bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring);
static void onIdle(void* userdata);
void dirtyRootFolder();
LLInventoryCategoriesObserver* mCategoriesObserver;
LLThumbnailsObserver* mThumbnailsObserver;
LLGalleryGestureObserver* mGestureObserver;
LLInventoryObserver* mInventoryObserver;
selection_deque mSelectedItemIDs;
selection_deque mItemsToSelect;
LLUUID mLastInteractedUUID;
bool mIsInitialized;
bool mRootDirty;
selection_change_signal_t mSelectionChangeSignal;
boost::signals2::signal<void()> mRootChangedSignal;
LLUUID mFolderID;
std::list<LLUUID> mBackwardFolders;
std::list<LLUUID> mForwardFolders;
private:
void addToGallery(LLInventoryGalleryItem* item);
void removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape = true);
void removeFromGalleryMiddle(LLInventoryGalleryItem* item);
LLPanel* addLastRow();
void removeLastRow();
void moveRowUp(int row);
void moveRowDown(int row);
void moveRow(int row, int pos);
LLPanel* addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap);
void removeFromLastRow(LLInventoryGalleryItem* item);
void reArrangeRows(S32 row_diff = 0);
bool updateRowsIfNeeded();
void updateGalleryWidth();
LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn);
void buildGalleryPanel(int row_count);
void reshapeGalleryPanel(int row_count);
LLPanel* buildItemPanel(int left);
LLPanel* buildRowPanel(int left, int bottom);
void moveRowPanel(LLPanel* stack, int left, int bottom);
std::vector<LLPanel*> mRowPanels;
std::vector<LLPanel*> mItemPanels;
std::vector<LLPanel*> mUnusedRowPanels;
std::vector<LLPanel*> mUnusedItemPanels;
std::vector<LLInventoryGalleryItem*> mItems;
std::vector<LLInventoryGalleryItem*> mHiddenItems;
LLScrollContainer* mScrollPanel;
LLPanel* mGalleryPanel;
LLPanel* mLastRowPanel;
LLTextBox* mMessageTextBox;
int mRowCount;
int mItemsAddedCount;
bool mGalleryCreated;
bool mNeedsArrange;
/* Params */
int mRowPanelHeight;
int mVerticalGap;
int mHorizontalGap;
int mItemWidth;
int mItemHeight;
int mItemHorizontalGap;
int mItemsInRow;
int mRowPanelWidth;
int mGalleryWidth;
int mRowPanWidthFactor;
int mGalleryWidthFactor;
LLInventoryGalleryContextMenu* mInventoryGalleryMenu;
LLInventoryGalleryContextMenu* mRootGalleryMenu;
std::string mFilterSubString;
LLInventoryFilter* mFilter;
U32 mSortOrder;
typedef std::map<LLUUID, LLInventoryGalleryItem*> gallery_item_map_t;
gallery_item_map_t mItemMap;
uuid_vec_t mCOFLinkedItems;
uuid_vec_t mActiveGestures;
uuid_set_t mItemBuildQuery;
std::map<LLInventoryGalleryItem*, S32> mItemIndexMap;
std::map<S32, LLInventoryGalleryItem*> mIndexToItemMap;
LLInventoryFilter::ESearchType mSearchType;
std::string mUsername;
};
class LLInventoryGalleryItem : public LLPanel
{
public:
struct Params : public LLInitParam::Block<Params, LLPanel::Params>
{};
enum EInventorySortGroup
{
SG_SYSTEM_FOLDER,
SG_TRASH_FOLDER,
SG_NORMAL_FOLDER,
SG_ITEM
};
LLInventoryGalleryItem(const Params& p);
virtual ~LLInventoryGalleryItem();
BOOL postBuild();
void draw();
BOOL handleMouseDown(S32 x, S32 y, MASK mask);
BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
BOOL handleMouseUp(S32 x, S32 y, MASK mask);
BOOL handleHover(S32 x, S32 y, MASK mask);
BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
std::string& tooltip_msg);
BOOL handleKeyHere(KEY key, MASK mask);
void onFocusLost();
void onFocusReceived();
LLFontGL* getTextFont();
void setItemName(std::string name);
bool isSelected() { return mSelected; }
void setSelected(bool value);
void setWorn(bool value);
void setUUID(LLUUID id) {mUUID = id;}
LLUUID getUUID() { return mUUID;}
void setAssetIDStr(std::string asset_id) {mAssetIDStr = asset_id;}
std::string getAssetIDStr() { return mAssetIDStr;}
void setDescription(std::string desc) {mDesc = desc;}
std::string getDescription() { return mDesc;}
void setCreatorName(std::string name) {mCreatorName = name;}
std::string getCreatorName() { return mCreatorName;}
void setCreationDate(time_t date) {mCreationDate = date;}
time_t getCreationDate() { return mCreationDate;}
std::string getItemName() {return mItemName;}
std::string getItemNameSuffix() {return mPermSuffix + mWornSuffix;}
bool isDefaultImage() {return mDefaultImage;}
bool isHidden() {return mHidden;}
void setHidden(bool hidden) {mHidden = hidden;}
void setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link);
LLAssetType::EType getAssetType() { return mType; }
void setThumbnail(LLUUID id);
void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; }
bool isFolder() { return mIsFolder; }
bool isLink() { return mIsLink; }
EInventorySortGroup getSortGroup() { return mSortGroup; }
void updateNameText();
private:
bool isFadeItem();
LLUUID mUUID;
LLTextBox* mNameText;
LLPanel* mTextBgPanel;
bool mSelected;
bool mWorn;
bool mDefaultImage;
bool mHidden;
bool mIsFolder;
bool mIsLink;
S32 mCutGeneration;
bool mSelectedForCut;
std::string mAssetIDStr;
std::string mDesc;
std::string mCreatorName;
time_t mCreationDate;
EInventorySortGroup mSortGroup;
LLAssetType::EType mType;
std::string mItemName;
std::string mWornSuffix;
std::string mPermSuffix;
LLInventoryGallery* mGallery;
};
class LLThumbnailsObserver : public LLInventoryObserver
{
public:
LLThumbnailsObserver(){};
virtual void changed(U32 mask);
bool addItem(const LLUUID& obj_id, callback_t cb);
void removeItem(const LLUUID& obj_id);
protected:
struct LLItemData
{
LLItemData(const LLUUID& obj_id, const LLUUID& thumbnail_id, callback_t cb)
: mItemID(obj_id)
, mCallback(cb)
, mThumbnailID(thumbnail_id)
{}
callback_t mCallback;
LLUUID mItemID;
LLUUID mThumbnailID;
};
typedef std::map<LLUUID, LLItemData> item_map_t;
typedef item_map_t::value_type item_map_value_t;
item_map_t mItemMap;
};
class LLGalleryGestureObserver : public LLGestureManagerObserver
{
public:
LLGalleryGestureObserver(LLInventoryGallery* gallery) : mGallery(gallery) {}
virtual ~LLGalleryGestureObserver() {}
virtual void changed() { mGallery->onGesturesChanged(); }
private:
LLInventoryGallery* mGallery;
};
#endif

View File

@ -0,0 +1,714 @@
/**
* @file llinventorygallerymenu.cpp
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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 "llinventorygallery.h"
#include "llinventorygallerymenu.h"
#include "llagent.h"
#include "llappearancemgr.h"
#include "llavataractions.h"
#include "llclipboard.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloaterworldmap.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "lllandmarkactions.h"
#include "llmarketplacefunctions.h"
#include "llmenugl.h"
#include "llnotificationsutil.h"
#include "llpreviewtexture.h"
#include "lltrans.h"
#include "llviewerfoldertype.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
LLContextMenu* LLInventoryGalleryContextMenu::createMenu()
{
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2));
registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2));
registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
std::set<LLUUID> uuids(mUUIDs.begin(), mUUIDs.end());
registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery)));
enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2));
LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml");
updateMenuItemsVisibility(menu);
return menu;
}
void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata)
{
std::string action = userdata.asString();
LLInventoryObject* obj = gInventory.getObject(mUUIDs.front());
if(!obj) return;
if ("open_selected_folder" == action)
{
mGallery->setRootFolder(mUUIDs.front());
}
else if ("open_in_new_window" == action)
{
new_folder_window(mUUIDs.front());
}
else if ("properties" == action)
{
show_item_profile(mUUIDs.front());
}
else if ("restore" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);
if (cat)
{
const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
// do not restamp children on restore
gInventory.changeCategoryParent(cat, new_parent, false);
}
else
{
LLViewerInventoryItem* item = gInventory.getItem(selected_id);
if (item)
{
bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT);
const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot ? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType()));
// do not restamp children on restore
gInventory.changeItemParent(item, new_parent, false);
}
}
}
}
else if ("copy_uuid" == action)
{
LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front());
if(item)
{
LLUUID asset_id = item->getProtectedAssetUUID();
std::string buffer;
asset_id.toString(buffer);
gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer));
}
}
else if ("purge" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
remove_inventory_object(selected_id, NULL);
}
}
else if ("goto" == action)
{
show_item_original(mUUIDs.front());
}
else if ("thumbnail" == action)
{
LLSD data(mUUIDs.front());
LLFloaterReg::showInstance("change_item_thumbnail", data);
}
else if ("cut" == action)
{
if (mGallery->canCut())
{
mGallery->cut();
}
}
else if ("paste" == action)
{
if (mGallery->canPaste())
{
mGallery->paste();
}
}
else if ("delete" == action)
{
mGallery->deleteSelection();
}
else if ("copy" == action)
{
if (mGallery->canCopy())
{
mGallery->copy();
}
}
else if ("paste_link" == action)
{
mGallery->pasteAsLink();
}
else if ("rename" == action)
{
rename(mUUIDs.front());
}
else if ("open" == action || "open_original" == action)
{
LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front());
if (item)
{
LLInvFVBridgeAction::doAction(item->getType(), mUUIDs.front(), &gInventory);
}
}
else if ("ungroup_folder_items" == action)
{
ungroup_folder_items(mUUIDs.front());
}
else if ("take_off" == action || "detach" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLAppearanceMgr::instance().removeItemFromAvatar(selected_id);
}
}
else if ("wear_add" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding.
}
}
else if ("wear" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true);
}
}
else if ("activate" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLGestureMgr::instance().activateGesture(selected_id);
LLViewerInventoryItem* item = gInventory.getItem(selected_id);
if (!item) return;
gInventory.updateItem(item);
}
gInventory.notifyObservers();
}
else if ("deactivate" == action)
{
for (LLUUID& selected_id : mUUIDs)
{
LLGestureMgr::instance().deactivateGesture(selected_id);
LLViewerInventoryItem* item = gInventory.getItem(selected_id);
if (!item) return;
gInventory.updateItem(item);
}
gInventory.notifyObservers();
}
else if ("replace_links" == action)
{
LLFloaterReg::showInstance("linkreplace", LLSD(mUUIDs.front()));
}
else if ("copy_slurl" == action)
{
boost::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark)
{
LLVector3d global_pos;
landmark->getGlobalPos(global_pos);
boost::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](const std::string& slurl)
{
gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
LLSD args;
args["SLURL"] = slurl;
LLNotificationsUtil::add("CopySLURL", args);
};
LLLandmarkActions::getSLURLfromPosGlobal(global_pos, copy_slurl_to_clipboard_cb, true);
};
LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), copy_slurl_cb);
if (landmark)
{
copy_slurl_cb(landmark);
}
}
else if ("about" == action)
{
LLSD key;
key["type"] = "landmark";
key["id"] = mUUIDs.front();
LLFloaterSidePanelContainer::showPanel("places", key);
}
else if ("show_on_map" == action)
{
boost::function<void(LLLandmark*)> show_on_map_cb = [](LLLandmark* landmark)
{
LLVector3d landmark_global_pos;
if (landmark->getGlobalPos(landmark_global_pos))
{
LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
if (!landmark_global_pos.isExactlyZero() && worldmap_instance)
{
worldmap_instance->trackLocation(landmark_global_pos);
LLFloaterReg::showInstance("world_map", "center");
}
}
};
LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), show_on_map_cb);
if(landmark)
{
show_on_map_cb(landmark);
}
}
else if ("save_as" == action)
{
LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUIDs.front());
if (preview_texture)
{
preview_texture->openToSave();
preview_texture->saveAs();
}
}
}
void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id)
{
LLInventoryObject* obj = gInventory.getObject(item_id);
if (!obj) return;
LLSD args;
args["NAME"] = obj->getName();
LLSD payload;
payload["id"] = mUUIDs.front();
LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2));
}
void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option != 0) return; // canceled
std::string new_name = response["new_name"].asString();
LLStringUtil::trim(new_name);
if (!new_name.empty())
{
LLUUID id = notification["payload"]["id"].asUUID();
LLViewerInventoryCategory* cat = gInventory.getCategory(id);
if(cat && (cat->getName() != new_name))
{
LLSD updates;
updates["name"] = new_name;
update_inventory_category(cat->getUUID(),updates, NULL);
return;
}
LLViewerInventoryItem* item = gInventory.getItem(id);
if(item && (item->getName() != new_name))
{
LLSD updates;
updates["name"] = new_name;
update_inventory_item(item->getUUID(),updates, NULL);
}
}
}
void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata)
{
const std::string param = userdata.asString();
if (param == "model")
{
gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString());
}
else if (param == "texture")
{
gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString());
}
else if (param == "sound")
{
gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString());
}
else if (param == "animation")
{
gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString());
}
}
bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata)
{
if (mUUIDs.size() != 1)
{
return false;
}
LLInventoryCategory* cat = gInventory.getCategory(mUUIDs.front());
if (!cat)
{
return false;
}
return true;
}
bool is_inbox_folder(LLUUID item_id)
{
const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);
if (inbox_id.isNull())
{
return false;
}
return gInventory.isObjectDescendentOf(item_id, inbox_id);
}
void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu)
{
LLUUID selected_id = mUUIDs.front();
LLInventoryObject* obj = gInventory.getObject(selected_id);
if (!obj)
{
return;
}
std::vector<std::string> items;
std::vector<std::string> disabled_items;
bool is_agent_inventory = gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID());
bool is_link = obj->getIsLinkType();
bool is_folder = (obj->getType() == LLAssetType::AT_CATEGORY);
bool is_cof = LLAppearanceMgr::instance().getIsInCOF(selected_id);
bool is_inbox = is_inbox_folder(selected_id);
bool is_trash = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
//bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
bool is_system_folder = false;
LLFolderType::EType folder_type(LLFolderType::FT_NONE);
bool has_children = false;
bool is_full_perm_item = false;
bool is_copyable = false;
LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id);
if(is_folder)
{
LLInventoryCategory* category = gInventory.getCategory(selected_id);
if (category)
{
folder_type = category->getPreferredType();
is_system_folder = LLFolderType::lookupIsProtectedType(folder_type);
has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO);
}
}
else
{
if (selected_item)
{
is_full_perm_item = selected_item->getIsFullPerm();
is_copyable = selected_item->getPermissions().allowCopyBy(gAgent.getID());
}
}
if(!is_link)
{
items.push_back(std::string("thumbnail"));
if (!is_agent_inventory || (is_in_trash && !is_trash))
{
disabled_items.push_back(std::string("thumbnail"));
}
}
if (is_folder)
{
if(!isRootFolder())
{
items.push_back(std::string("Copy Separator"));
items.push_back(std::string("open_in_current_window"));
items.push_back(std::string("open_in_new_window"));
items.push_back(std::string("Open Folder Separator"));
}
}
else
{
if (is_agent_inventory && (obj->getType() != LLAssetType::AT_LINK_FOLDER))
{
items.push_back(std::string("Replace Links"));
}
if (obj->getType() == LLAssetType::AT_LANDMARK)
{
items.push_back(std::string("Landmark Separator"));
items.push_back(std::string("url_copy"));
items.push_back(std::string("About Landmark"));
items.push_back(std::string("show_on_map"));
}
}
if(is_trash)
{
items.push_back(std::string("Empty Trash"));
LLInventoryModel::cat_array_t* cat_array;
LLInventoryModel::item_array_t* item_array;
gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array);
if (0 == cat_array->size() && 0 == item_array->size())
{
disabled_items.push_back(std::string("Empty Trash"));
}
}
else if(is_in_trash)
{
if (is_link)
{
items.push_back(std::string("Find Original"));
if (LLAssetType::lookupIsLinkType(obj->getType()))
{
disabled_items.push_back(std::string("Find Original"));
}
}
items.push_back(std::string("Purge Item"));
if (is_folder && !get_is_category_removable(&gInventory, selected_id))
{
disabled_items.push_back(std::string("Purge Item"));
}
items.push_back(std::string("Restore Item"));
}
else
{
if(can_share_item(selected_id))
{
items.push_back(std::string("Share"));
}
if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox_folder(selected_id))
{
items.push_back(std::string("Paste"));
static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
if (inventory_linking)
{
items.push_back(std::string("Paste As Link"));
}
}
if (is_folder && is_agent_inventory)
{
if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox_folder(selected_id))
{
if (!gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD)) && !isRootFolder())
{
items.push_back(std::string("New Folder"));
}
items.push_back(std::string("upload_def"));
}
if(is_outfits && !isRootFolder())
{
items.push_back(std::string("New Outfit"));
}
items.push_back(std::string("Subfolder Separator"));
if (!is_system_folder && !isRootFolder())
{
if(has_children && (folder_type != LLFolderType::FT_OUTFIT))
{
items.push_back(std::string("Ungroup folder items"));
}
items.push_back(std::string("Cut"));
items.push_back(std::string("Delete"));
if(!get_is_category_removable(&gInventory, selected_id))
{
disabled_items.push_back(std::string("Delete"));
disabled_items.push_back(std::string("Cut"));
}
if(!is_inbox)
{
items.push_back(std::string("Rename"));
}
}
if(!is_system_folder)
{
items.push_back(std::string("Copy"));
}
}
else if(!is_folder)
{
items.push_back(std::string("Properties"));
items.push_back(std::string("Copy Asset UUID"));
items.push_back(std::string("Copy Separator"));
bool is_asset_knowable = is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType());
if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308
|| (! ( is_full_perm_item || gAgent.isGodlike())))
{
disabled_items.push_back(std::string("Copy Asset UUID"));
}
if(is_agent_inventory)
{
items.push_back(std::string("Cut"));
if (!is_link || !is_cof || !get_is_item_worn(selected_id))
{
items.push_back(std::string("Delete"));
}
if(!get_is_item_removable(&gInventory, selected_id))
{
disabled_items.push_back(std::string("Delete"));
disabled_items.push_back(std::string("Cut"));
}
if (selected_item && (selected_item->getInventoryType() != LLInventoryType::IT_CALLINGCARD) && !is_inbox && selected_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()))
{
items.push_back(std::string("Rename"));
}
}
items.push_back(std::string("Copy"));
if (!is_copyable)
{
disabled_items.push_back(std::string("Copy"));
}
}
if((obj->getType() == LLAssetType::AT_SETTINGS)
|| ((obj->getType() <= LLAssetType::AT_GESTURE)
&& obj->getType() != LLAssetType::AT_OBJECT
&& obj->getType() != LLAssetType::AT_CLOTHING
&& obj->getType() != LLAssetType::AT_CATEGORY
&& obj->getType() != LLAssetType::AT_LANDMARK
&& obj->getType() != LLAssetType::AT_BODYPART))
{
bool can_open = !LLAssetType::lookupIsLinkType(obj->getType());
if (can_open)
{
if (is_link)
items.push_back(std::string("Open Original"));
else
items.push_back(std::string("Open"));
}
else
{
disabled_items.push_back(std::string("Open"));
disabled_items.push_back(std::string("Open Original"));
}
if(LLAssetType::AT_GESTURE == obj->getType())
{
items.push_back(std::string("Gesture Separator"));
if(!LLGestureMgr::instance().isGestureActive(selected_id))
{
items.push_back(std::string("Activate"));
}
else
{
items.push_back(std::string("Deactivate"));
}
}
}
else if(LLAssetType::AT_LANDMARK == obj->getType())
{
items.push_back(std::string("Landmark Open"));
}
else if (obj->getType() == LLAssetType::AT_OBJECT || obj->getType() == LLAssetType::AT_CLOTHING || obj->getType() == LLAssetType::AT_BODYPART)
{
items.push_back(std::string("Wearable And Object Separator"));
if(obj->getType() == LLAssetType::AT_CLOTHING)
{
items.push_back(std::string("Take Off"));
}
if(get_is_item_worn(selected_id))
{
if(obj->getType() == LLAssetType::AT_OBJECT)
{
items.push_back(std::string("Detach From Yourself"));
}
disabled_items.push_back(std::string("Wearable And Object Wear"));
disabled_items.push_back(std::string("Wearable Add"));
}
else
{
if(obj->getType() == LLAssetType::AT_OBJECT)
{
items.push_back(std::string("Wearable Add"));
}
items.push_back(std::string("Wearable And Object Wear"));
disabled_items.push_back(std::string("Take Off"));
}
if (!gAgentAvatarp->canAttachMoreObjects() && (obj->getType() == LLAssetType::AT_OBJECT))
{
disabled_items.push_back(std::string("Wearable And Object Wear"));
disabled_items.push_back(std::string("Wearable Add"));
}
if (selected_item && (obj->getType() != LLAssetType::AT_OBJECT) && LLWearableType::getInstance()->getAllowMultiwear(selected_item->getWearableType()))
{
items.push_back(std::string("Wearable Add"));
if (!gAgentWearables.canAddWearable(selected_item->getWearableType()))
{
disabled_items.push_back(std::string("Wearable Add"));
}
}
}
if(obj->getType() == LLAssetType::AT_TEXTURE)
{
items.push_back(std::string("Save As"));
bool can_copy = selected_item && selected_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED);
if (!can_copy)
{
disabled_items.push_back(std::string("Save As"));
}
}
if (is_link)
{
items.push_back(std::string("Find Original"));
if (LLAssetType::lookupIsLinkType(obj->getType()))
{
disabled_items.push_back(std::string("Find Original"));
}
}
if (is_lost_and_found)
{
items.push_back(std::string("Empty Lost And Found"));
LLInventoryModel::cat_array_t* cat_array;
LLInventoryModel::item_array_t* item_array;
gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array);
// Enable Empty menu item only when there is something to act upon.
if (0 == cat_array->size() && 0 == item_array->size())
{
disabled_items.push_back(std::string("Empty Lost And Found"));
}
disabled_items.push_back(std::string("New Folder"));
disabled_items.push_back(std::string("upload_def"));
}
}
hide_context_entries(*menu, items, disabled_items);
}

View File

@ -0,0 +1,62 @@
/**
* @file llinventorygallerymenu.h
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2023, 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_LLINVENTORYGALLERYMENU_H
#define LL_LLINVENTORYGALLERYMENU_H
#include "lllistcontextmenu.h"
class LLInventoryGalleryContextMenu : public LLListContextMenu
{
public:
LLInventoryGalleryContextMenu(LLInventoryGallery* gallery)
: LLListContextMenu(),
mGallery(gallery),
mRootFolder(false){}
/*virtual*/ LLContextMenu* createMenu();
bool isRootFolder() { return mRootFolder; }
void setRootFolder(bool is_root) { mRootFolder = is_root; }
void doToSelected(const LLSD& userdata);
void rename(const LLUUID& item_id);
protected:
//virtual void buildContextMenu(class LLMenuGL& menu, U32 flags);
void updateMenuItemsVisibility(LLContextMenu* menu);
void fileUploadLocation(const LLSD& userdata);
bool canSetUploadLocation(const LLSD& userdata);
static void onRename(const LLSD& notification, const LLSD& response);
private:
bool enableContextMenuItem(const LLSD& userdata);
bool checkContextMenuItem(const LLSD& userdata);
LLInventoryGallery* mGallery;
bool mRootFolder;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -226,10 +226,14 @@ private:
//--------------------------------------------------------------------
public:
static BOOL getIsFirstTimeInViewer2();
static bool isSysFoldersReady() { return (sPendingSystemFolders == 0); }
private:
static BOOL sFirstTimeInViewer2;
const static S32 sCurrentInvCacheVersion; // expected inventory cache version
static S32 sPendingSystemFolders;
/** Initialization/Setup
** **
*******************************************************************************/
@ -255,6 +259,7 @@ public:
void getDirectDescendentsOf(const LLUUID& cat_id,
cat_array_t*& categories,
item_array_t*& items) const;
void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const;
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
// Compute a hash of direct descendant names (for detecting child name changes)
@ -302,24 +307,25 @@ public:
// Find
//--------------------------------------------------------------------
public:
// Checks if category exists ("My Inventory" only), if it does not, creates it
void ensureCategoryForTypeExists(LLFolderType::EType preferred_type);
const LLUUID findCategoryUUIDForTypeInRoot(
LLFolderType::EType preferred_type,
bool create_folder,
const LLUUID& root_id);
const LLUUID& root_id) const;
// Returns the uuid of the category that specifies 'type' as what it
// defaults to containing. The category is not necessarily only for that type.
// NOTE: If create_folder is true, this will create a new inventory category
// on the fly if one does not exist. *NOTE: if find_in_library is true it
// will search in the user's library folder instead of "My Inventory"
const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type,
bool create_folder = true);
const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// will search in the user's library folder instead of "My Inventory"
const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type,
bool create_folder = true);
const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// Returns user specified category for uploads, returns default id if there are no
// user specified one or it does not exist, creates default category if it is missing.
const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type);
const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const;
// Get whatever special folder this object is a child of, if any.
const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const;
@ -405,13 +411,15 @@ public:
const LLUUID& new_parent_id,
BOOL restamp);
// Marks links from a "possibly" broken list for a rebuild
// clears the list
void rebuildBrockenLinks();
bool hasPosiblyBrockenLinks() const { return mPossiblyBrockenLinks.size() > 0; }
//--------------------------------------------------------------------
// Delete
//--------------------------------------------------------------------
public:
// Update model after an AISv3 update received for any operation.
void onAISUpdateReceived(const std::string& context, const LLSD& update);
// Update model after an item is confirmed as removed from
// server. Works for categories or items.
@ -475,10 +483,11 @@ public:
public:
// Returns the UUID of the new category. If you want to use the default
// name based on type, pass in a NULL to the 'name' parameter.
LLUUID createNewCategory(const LLUUID& parent_id,
void createNewCategory(const LLUUID& parent_id,
LLFolderType::EType preferred_type,
const std::string& name,
inventory_func_type callback = NULL);
inventory_func_type callback = NULL,
const LLUUID& thumbnail_id = LLUUID::null);
protected:
// Internal methods that add inventory and make sure that all of
// the internal data structures are consistent. These methods
@ -575,6 +584,10 @@ private:
U32 mModifyMaskBacklog;
changed_items_t mChangedItemIDsBacklog;
changed_items_t mAddedItemIDsBacklog;
typedef std::map<LLUUID , changed_items_t> broken_links_t;
broken_links_t mPossiblyBrockenLinks; // there can be multiple links per item
changed_items_t mLinksRebuildList;
boost::signals2::connection mBulkFecthCallbackSlot;
//--------------------------------------------------------------------
@ -663,7 +676,6 @@ public:
static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**);
static void removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label);
static void processRemoveInventoryItem(LLMessageSystem* msg, void**);
static void processUpdateInventoryFolder(LLMessageSystem* msg, void**);
static void removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg);
static void processRemoveInventoryFolder(LLMessageSystem* msg, void**);
static void processRemoveInventoryObjects(LLMessageSystem* msg, void**);

View File

@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llaisapi.h"
#include "llagent.h"
#include "llappviewer.h"
#include "llcallbacklist.h"
@ -186,12 +187,14 @@ const char * const LOG_INV("Inventory");
///----------------------------------------------------------------------------
LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch():
mBackgroundFetchActive(FALSE),
mBackgroundFetchActive(false),
mFolderFetchActive(false),
mFetchCount(0),
mAllFoldersFetched(FALSE),
mRecursiveInventoryFetchStarted(FALSE),
mRecursiveLibraryFetchStarted(FALSE),
mLastFetchCount(0),
mFetchFolderCount(0),
mAllRecursiveFoldersFetched(false),
mRecursiveInventoryFetchStarted(false),
mRecursiveLibraryFetchStarted(false),
mMinTimeBetweenFetches(0.3f)
{}
@ -200,7 +203,12 @@ LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch()
bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const
{
return mFetchQueue.empty() && mFetchCount <= 0;
return mFetchFolderQueue.empty() && mFetchItemQueue.empty() && mFetchCount <= 0;
}
bool LLInventoryModelBackgroundFetch::isFolderFetchProcessingComplete() const
{
return mFetchFolderQueue.empty() && mFetchFolderCount <= 0;
}
bool LLInventoryModelBackgroundFetch::libraryFetchStarted() const
@ -235,7 +243,7 @@ bool LLInventoryModelBackgroundFetch::inventoryFetchInProgress() const
bool LLInventoryModelBackgroundFetch::isEverythingFetched() const
{
return mAllFoldersFetched;
return mAllRecursiveFoldersFetched;
}
BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const
@ -243,17 +251,33 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const
return mFolderFetchActive;
}
void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category)
void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, bool recursive, bool is_category)
{
mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category));
EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
if (is_category)
{
mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type, is_category));
}
else
{
mFetchItemQueue.push_front(FetchQueueInfo(id, recursion_type, is_category));
}
}
void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category)
void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, bool recursive, bool is_category)
{
mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category));
EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
if (is_category)
{
mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type, is_category));
}
else
{
mFetchItemQueue.push_back(FetchQueueInfo(id, recursion_type, is_category));
}
}
void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive)
{
LLViewerInventoryCategory * cat(gInventory.getCategory(id));
@ -262,31 +286,53 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
// it's a folder, do a bulk fetch
LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL;
mBackgroundFetchActive = TRUE;
mBackgroundFetchActive = true;
mFolderFetchActive = true;
EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;
if (id.isNull())
{
if (! mRecursiveInventoryFetchStarted)
{
mRecursiveInventoryFetchStarted |= recursive;
mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive));
if (recursive && AISAPI::isAvailable())
{
// Not only root folder can be massive, but
// most system folders will be requested independently
// so request root folder and content separately
mFetchFolderQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), FT_FOLDER_AND_CONTENT));
}
else
{
mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursion_type));
}
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
if (! mRecursiveLibraryFetchStarted)
{
mRecursiveLibraryFetchStarted |= recursive;
mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive));
mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursion_type));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
else
{
// Specific folder requests go to front of queue.
if (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)
{
mFetchQueue.push_front(FetchQueueInfo(id, recursive));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
if (AISAPI::isAvailable())
{
if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != id)
{
// On AIS make sure root goes to the top and follow up recursive
// fetches, not individual requests
mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
else if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != id)
{
// Specific folder requests go to front of queue.
mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
if (id == gInventory.getLibraryRootFolderID())
{
mRecursiveLibraryFetchStarted |= recursive;
@ -299,21 +345,41 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)
}
else if (LLViewerInventoryItem * itemp = gInventory.getItem(id))
{
if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id))
if (! itemp->mIsComplete)
{
mBackgroundFetchActive = TRUE;
mFetchQueue.push_front(FetchQueueInfo(id, false, false));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
scheduleItemFetch(id);
}
}
}
void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id, bool forced)
{
if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
{
mBackgroundFetchActive = true;
// Specific folder requests go to front of queue.
mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, bool forced)
{
if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != item_id)
{
mBackgroundFetchActive = true;
mFetchItemQueue.push_front(FetchQueueInfo(item_id, forced ? FT_FORCED : FT_DEFAULT, false));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
void LLInventoryModelBackgroundFetch::findLostItems()
{
mBackgroundFetchActive = TRUE;
mFolderFetchActive = true;
mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, TRUE));
mBackgroundFetchActive = true;
mFolderFetchActive = true;
mFetchFolderQueue.push_back(FetchQueueInfo(LLUUID::null, FT_RECURSIVE));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
@ -322,15 +388,28 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched()
if (mRecursiveInventoryFetchStarted &&
mRecursiveLibraryFetchStarted)
{
mAllFoldersFetched = TRUE;
mAllRecursiveFoldersFetched = true;
//LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL;
//gInventory.validate();
}
mFolderFetchActive = false;
mBackgroundFetchActive = false;
if (isBulkFetchProcessingComplete())
{
mBackgroundFetchActive = false;
}
// For now only informs about initial fetch being done
mFoldersFetchedSignal();
LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL;
}
boost::signals2::connection LLInventoryModelBackgroundFetch::setFetchCompletionCallback(folders_fetched_callback_t cb)
{
return mFoldersFetchedSignal.connect(cb);
}
void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)
{
LLInventoryModelBackgroundFetch::instance().backgroundFetch();
@ -338,10 +417,17 @@ void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)
void LLInventoryModelBackgroundFetch::backgroundFetch()
{
if (mBackgroundFetchActive && gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived())
if (mBackgroundFetchActive)
{
// If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
bulkFetch();
if (AISAPI::isAvailable())
{
bulkFetchViaAis();
}
else if (gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived())
{
// If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
bulkFetch();
}
}
}
@ -354,9 +440,476 @@ void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching)
mFetchCount = 0;
}
}
void LLInventoryModelBackgroundFetch::incrFetchFolderCount(S32 fetching)
{
incrFetchCount(fetching);
mFetchFolderCount += fetching;
if (mFetchCount < 0)
{
LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL;
mFetchFolderCount = 0;
}
}
void ais_simple_item_callback(const LLUUID& inv_id)
{
LL_DEBUGS(LOG_INV , "AIS3") << "Response for " << inv_id << LL_ENDL;
LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1);
}
void LLInventoryModelBackgroundFetch::onAISContentCalback(
const LLUUID& request_id,
const uuid_vec_t& content_ids,
const LLUUID& response_id,
EFetchType fetch_type)
{
// Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis
incrFetchFolderCount(-1);
uuid_vec_t::const_iterator folder_iter = content_ids.begin();
uuid_vec_t::const_iterator folder_end = content_ids.end();
while (folder_iter != folder_end)
{
std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), *folder_iter);
if (found != mExpectedFolderIds.end())
{
mExpectedFolderIds.erase(found);
}
LLViewerInventoryCategory* cat(gInventory.getCategory(*folder_iter));
if (cat)
{
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
}
if (response_id.isNull())
{
// Failed to fetch, get it individually
mFetchFolderQueue.push_back(FetchQueueInfo(*folder_iter, FT_RECURSIVE));
}
else
{
// push descendant back to verify they are fetched fully (ex: didn't encounter depth limit)
LLInventoryModel::cat_array_t* categories(NULL);
LLInventoryModel::item_array_t* items(NULL);
gInventory.getDirectDescendentsOf(*folder_iter, categories, items);
if (categories)
{
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
it != categories->end();
++it)
{
mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
}
}
}
folder_iter++;
}
if (!mFetchFolderQueue.empty())
{
mBackgroundFetchActive = true;
mFolderFetchActive = true;
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type)
{
// Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis
incrFetchFolderCount(-1);
std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), request_id);
if (found != mExpectedFolderIds.end())
{
mExpectedFolderIds.erase(found);
}
else
{
// ais shouldn't respond twice
llassert(false);
LL_WARNS() << "Unexpected folder response for " << request_id << LL_ENDL;
}
if (request_id.isNull())
{
// orhans, no other actions needed
return;
}
bool request_descendants = false;
if (response_id.isNull()) // Failure
{
LL_DEBUGS(LOG_INV , "AIS3") << "Failure response for folder " << request_id << LL_ENDL;
if (fetch_type == FT_RECURSIVE)
{
// A full recursive request failed.
// Try requesting folder and nested content separately
mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_FOLDER_AND_CONTENT));
}
else if (fetch_type == FT_FOLDER_AND_CONTENT)
{
LL_WARNS() << "Failed to download folder: " << request_id << " Requesting known content separately" << LL_ENDL;
mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
// set folder's version to prevent viewer from trying to request folder indefinetely
LLViewerInventoryCategory* cat(gInventory.getCategory(request_id));
if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
cat->setVersion(0);
}
}
}
else
{
if (fetch_type == FT_RECURSIVE)
{
// Got the folder and content, now verify content
// Request content even for FT_RECURSIVE in case of changes, failures
// or if depth limit gets imlemented.
// This shouldn't redownload folders if they already have version
request_descendants = true;
LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << ". Requesting content" << LL_ENDL;
}
else if (fetch_type == FT_FOLDER_AND_CONTENT)
{
// readd folder for content request
mFetchFolderQueue.push_front(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
}
else
{
LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << "." << LL_ENDL;
}
}
if (request_descendants)
{
LLInventoryModel::cat_array_t* categories(NULL);
LLInventoryModel::item_array_t* items(NULL);
gInventory.getDirectDescendentsOf(request_id, categories, items);
if (categories)
{
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
it != categories->end();
++it)
{
mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
}
}
}
if (!mFetchFolderQueue.empty())
{
mBackgroundFetchActive = true;
mFolderFetchActive = true;
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
// done
LLViewerInventoryCategory * cat(gInventory.getCategory(request_id));
if (cat)
{
cat->setFetching(LLViewerInventoryCategory::FETCH_NONE);
}
}
static LLTrace::BlockTimerStatHandle FTM_BULK_FETCH("Bulk Fetch");
void LLInventoryModelBackgroundFetch::bulkFetchViaAis()
{
LL_RECORD_BLOCK_TIME(FTM_BULK_FETCH);
//Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
if (gDisconnected)
{
return;
}
static LLCachedControl<U32> ais_pool(gSavedSettings, "PoolSizeAIS", 20);
// Don't have too many requests at once, AIS throttles
// Reserve one request for actions outside of fetch (like renames)
const U32 max_concurrent_fetches = llclamp(ais_pool - 1, 1, 50);
if (mFetchCount >= max_concurrent_fetches)
{
return;
}
// Don't loop for too long (in case of large, fully loaded inventory)
F64 curent_time = LLTimer::getTotalSeconds();
const F64 max_time = LLStartUp::getStartupState() > STATE_WEARABLES_WAIT
? 0.006f // 6 ms
: 1.f;
const F64 end_time = curent_time + max_time;
S32 last_fetch_count = mFetchCount;
while (!mFetchFolderQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time)
{
const FetchQueueInfo & fetch_info(mFetchFolderQueue.front());
bulkFetchViaAis(fetch_info);
mFetchFolderQueue.pop_front();
curent_time = LLTimer::getTotalSeconds();
}
// Ideally we shouldn't fetch items if recursive fetch isn't done,
// but there is a chance some request will start timeouting and recursive
// fetch will get stuck on a signle folder, don't block item fetch in such case
while (!mFetchItemQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time)
{
const FetchQueueInfo& fetch_info(mFetchItemQueue.front());
bulkFetchViaAis(fetch_info);
mFetchItemQueue.pop_front();
curent_time = LLTimer::getTotalSeconds();
}
if (last_fetch_count != mFetchCount // if anything was added
|| mLastFetchCount != mFetchCount) // if anything was substracted
{
LL_DEBUGS(LOG_INV , "AIS3") << "Total active fetches: " << mLastFetchCount << "->" << last_fetch_count << "->" << mFetchCount
<< ", scheduled folder fetches: " << (S32)mFetchFolderQueue.size()
<< ", scheduled item fetches: " << (S32)mFetchItemQueue.size()
<< LL_ENDL;
mLastFetchCount = mFetchCount;
if (!mExpectedFolderIds.empty())
{
// A folder seem to be stack fetching on QA account, print oldest folder out
LL_DEBUGS(LOG_INV , "AIS3") << "Oldest expected folder: ";
std::list<LLUUID>::const_iterator iter = mExpectedFolderIds.begin();
LL_CONT << *iter;
if ((*iter).notNull())
{
LLViewerInventoryCategory* cat(gInventory.getCategory(*iter));
if (cat)
{
LL_CONT << " Folder name: " << cat->getName() << " Parent: " << cat->getParentUUID();
}
else
{
LL_CONT << " This folder doesn't exist";
}
}
else
{
LL_CONT << " Orphans request";
}
LL_CONT << LL_ENDL;
}
}
if (isFolderFetchProcessingComplete() && mFolderFetchActive)
{
setAllFoldersFetched();
}
if (isBulkFetchProcessingComplete())
{
mBackgroundFetchActive = false;
}
}
void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetch_info)
{
if (fetch_info.mIsCategory)
{
const LLUUID & cat_id(fetch_info.mUUID);
if (cat_id.isNull())
{
incrFetchFolderCount(1);
mExpectedFolderIds.push_back(cat_id);
// Lost and found
// Should it actually be recursive?
AISAPI::FetchOrphans([](const LLUUID& response_id)
{
LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(LLUUID::null,
response_id,
FT_DEFAULT);
});
}
else
{
LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
if (cat)
{
if (fetch_info.mFetchType == FT_CONTENT_RECURSIVE)
{
// fetch content only, ignore cat itself
uuid_vec_t children;
LLInventoryModel::cat_array_t* categories(NULL);
LLInventoryModel::item_array_t* items(NULL);
gInventory.getDirectDescendentsOf(cat_id, categories, items);
LLViewerInventoryCategory::EFetchType target_state = LLViewerInventoryCategory::FETCH_RECURSIVE;
bool content_done = true;
// Top limit is 'as many as you can put into url'
static LLCachedControl<S32> ais_batch(gSavedSettings, "BatchSizeAIS3", 20);
S32 batch_limit = llclamp(ais_batch(), 1, 40);
for (LLInventoryModel::cat_array_t::iterator it = categories->begin();
it != categories->end();
++it)
{
LLViewerInventoryCategory* child_cat = (*it);
if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion()
|| child_cat->getFetching() >= target_state)
{
continue;
}
if (child_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS)
{
// special case
content_done = false;
if (children.empty())
{
// fetch marketplace alone
// Should it actually be fetched as FT_FOLDER_AND_CONTENT?
children.push_back(child_cat->getUUID());
mExpectedFolderIds.push_back(child_cat->getUUID());
child_cat->setFetching(target_state);
break;
}
else
{
// fetch marketplace alone next run
continue;
}
}
children.push_back(child_cat->getUUID());
mExpectedFolderIds.push_back(child_cat->getUUID());
child_cat->setFetching(target_state);
if (children.size() >= batch_limit)
{
content_done = false;
break;
}
}
if (!children.empty())
{
// increment before call in case of immediate callback
incrFetchFolderCount(1);
EFetchType type = fetch_info.mFetchType;
LLUUID cat_id = cat->getUUID(); // need a copy for lambda
AISAPI::completion_t cb = [cat_id, children, type](const LLUUID& response_id)
{
LLInventoryModelBackgroundFetch::instance().onAISContentCalback(cat_id, children, response_id, type);
};
AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY;
if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
{
item_type = AISAPI::LIBRARY;
}
AISAPI::FetchCategorySubset(cat_id, children, item_type, true, cb, 0);
}
if (content_done)
{
// This will have a bit of overlap with onAISContentCalback,
// but something else might have dowloaded folders, so verify
// every child that is complete has it's children done as well
for (LLInventoryModel::cat_array_t::iterator it = categories->begin();
it != categories->end();
++it)
{
LLViewerInventoryCategory* child_cat = (*it);
if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion())
{
mFetchFolderQueue.push_back(FetchQueueInfo(child_cat->getUUID(), FT_RECURSIVE));
}
}
}
else
{
// send it back to get the rest
mFetchFolderQueue.push_back(FetchQueueInfo(cat_id, FT_CONTENT_RECURSIVE));
}
}
else if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()
|| fetch_info.mFetchType == FT_FORCED)
{
LLViewerInventoryCategory::EFetchType target_state =
fetch_info.mFetchType > FT_CONTENT_RECURSIVE
? LLViewerInventoryCategory::FETCH_RECURSIVE
: LLViewerInventoryCategory::FETCH_NORMAL;
// start again if we did a non-recursive fetch before
// to get all children in a single request
if (cat->getFetching() < target_state)
{
// increment before call in case of immediate callback
incrFetchFolderCount(1);
cat->setFetching(target_state);
mExpectedFolderIds.push_back(cat_id);
EFetchType type = fetch_info.mFetchType;
LLUUID cat_id = cat->getUUID();
AISAPI::completion_t cb = [cat_id , type](const LLUUID& response_id)
{
LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_id , response_id , type);
};
AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY;
if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
{
item_type = AISAPI::LIBRARY;
}
AISAPI::FetchCategoryChildren(cat_id , item_type , type == FT_RECURSIVE , cb, 0);
}
}
else
{
// Already fetched, check if anything inside needs fetching
if (fetch_info.mFetchType == FT_RECURSIVE
|| fetch_info.mFetchType == FT_FOLDER_AND_CONTENT)
{
LLInventoryModel::cat_array_t * categories(NULL);
LLInventoryModel::item_array_t * items(NULL);
gInventory.getDirectDescendentsOf(cat_id, categories, items);
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
it != categories->end();
++it)
{
// not push_front to not cause an infinite loop
mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
}
}
}
} // else try to fetch folder either way?
}
}
else
{
LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
if (itemp)
{
if (!itemp->isFinished() || fetch_info.mFetchType == FT_FORCED)
{
mFetchCount++;
if (itemp->getPermissions().getOwner() == gAgent.getID())
{
AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback);
}
else
{
AISAPI::FetchItem(fetch_info.mUUID, AISAPI::LIBRARY, ais_simple_item_callback);
}
}
}
else // We don't know it, assume incomplete
{
// Assume agent's inventory, library wouldn't have gotten here
mFetchCount++;
AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback);
}
}
}
// Bundle up a bunch of requests to send all at once.
void LLInventoryModelBackgroundFetch::bulkFetch()
{
@ -376,13 +929,6 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
// inventory more quickly.
static const U32 max_batch_size(10);
static const S32 max_concurrent_fetches(12); // Outstanding requests, not connections
static const F32 new_min_time(0.05f); // *HACK: Clean this up when old code goes away entirely.
mMinTimeBetweenFetches = new_min_time;
if (mMinTimeBetweenFetches < new_min_time)
{
mMinTimeBetweenFetches = new_min_time; // *HACK: See above.
}
if (mFetchCount)
{
@ -396,8 +942,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
gInventory.notifyObservers();
}
if ((mFetchCount > max_concurrent_fetches) ||
(mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches))
if (mFetchCount > max_concurrent_fetches)
{
return;
}
@ -417,95 +962,105 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
LLSD item_request_body;
LLSD item_request_body_lib;
while (! mFetchQueue.empty()
while (! mFetchFolderQueue.empty()
&& (item_count + folder_count) < max_batch_size)
{
const FetchQueueInfo & fetch_info(mFetchQueue.front());
const FetchQueueInfo & fetch_info(mFetchFolderQueue.front());
if (fetch_info.mIsCategory)
{
const LLUUID & cat_id(fetch_info.mUUID);
if (cat_id.isNull()) //DEV-17797
if (cat_id.isNull()) //DEV-17797 Lost and found
{
LLSD folder_sd;
folder_sd["folder_id"] = LLUUID::null.asString();
folder_sd["owner_id"] = gAgent.getID();
folder_sd["sort_order"] = LLSD::Integer(sort_order);
folder_sd["fetch_folders"] = LLSD::Boolean(FALSE);
folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
folder_sd["fetch_folders"] = LLSD::Boolean(false);
folder_sd["fetch_items"] = LLSD::Boolean(true);
folder_request_body["folders"].append(folder_sd);
folder_count++;
}
else
{
const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
if (cat)
{
if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()
&& std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end())
{
LLSD folder_sd;
folder_sd["folder_id"] = cat->getUUID();
folder_sd["owner_id"] = cat->getOwnerID();
folder_sd["sort_order"] = LLSD::Integer(sort_order);
folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted;
folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
{
folder_request_body_lib["folders"].append(folder_sd);
}
else
{
folder_request_body["folders"].append(folder_sd);
}
folder_count++;
}
const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
if (cat)
{
if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
{
if (std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end())
{
LLSD folder_sd;
folder_sd["folder_id"] = cat->getUUID();
folder_sd["owner_id"] = cat->getOwnerID();
folder_sd["sort_order"] = LLSD::Integer(sort_order);
folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted;
folder_sd["fetch_items"] = LLSD::Boolean(TRUE);
// May already have this folder, but append child folders to list.
if (fetch_info.mRecursive)
{
LLInventoryModel::cat_array_t * categories(NULL);
LLInventoryModel::item_array_t * items(NULL);
gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
it != categories->end();
++it)
{
mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive));
}
}
}
if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
{
folder_request_body_lib["folders"].append(folder_sd);
}
else
{
folder_request_body["folders"].append(folder_sd);
}
folder_count++;
}
}
else
{
// May already have this folder, but append child folders to list.
if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
{
LLInventoryModel::cat_array_t * categories(NULL);
LLInventoryModel::item_array_t * items(NULL);
gInventory.getDirectDescendentsOf(cat_id, categories, items);
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
it != categories->end();
++it)
{
mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mFetchType));
}
}
}
}
}
if (fetch_info.mRecursive)
if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
{
recursive_cats.push_back(cat_id);
}
all_cats.push_back(cat_id);
}
else
{
LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
if (itemp)
{
LLSD item_sd;
item_sd["owner_id"] = itemp->getPermissions().getOwner();
item_sd["item_id"] = itemp->getUUID();
if (itemp->getPermissions().getOwner() == gAgent.getID())
{
item_request_body.append(item_sd);
}
else
{
item_request_body_lib.append(item_sd);
}
//itemp->fetchFromServer();
item_count++;
}
}
mFetchFolderQueue.pop_front();
}
mFetchQueue.pop_front();
while (!mFetchItemQueue.empty()
&& (item_count + folder_count) < max_batch_size)
{
const FetchQueueInfo & fetch_info(mFetchItemQueue.front());
LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID));
if (itemp)
{
LLSD item_sd;
item_sd["owner_id"] = itemp->getPermissions().getOwner();
item_sd["item_id"] = itemp->getUUID();
if (itemp->getPermissions().getOwner() == gAgent.getID())
{
item_request_body.append(item_sd);
}
else
{
item_request_body_lib.append(item_sd);
}
//itemp->fetchFromServer();
item_count++;
}
mFetchItemQueue.pop_front();
}
// Issue HTTP POST requests to fetch folders and items
@ -576,14 +1131,22 @@ void LLInventoryModelBackgroundFetch::bulkFetch()
bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID & cat_id) const
{
for (fetch_queue_t::const_iterator it = mFetchQueue.begin();
it != mFetchQueue.end();
for (fetch_queue_t::const_iterator it = mFetchFolderQueue.begin();
it != mFetchFolderQueue.end();
++it)
{
const LLUUID & fetch_id = (*it).mUUID;
if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
return false;
}
for (fetch_queue_t::const_iterator it = mFetchItemQueue.begin();
it != mFetchItemQueue.end();
++it)
{
const LLUUID & fetch_id = (*it).mUUID;
if (gInventory.isObjectDescendentOf(fetch_id, cat_id))
return false;
}
return true;
}
@ -829,10 +1392,10 @@ void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::Http
while (iter != end)
{
folders.append(*iter);
LLUUID fodler_id = iter->get("folder_id").asUUID();
if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), fodler_id) != mRecursiveCatUUIDs.end())
LLUUID folder_id = iter->get("folder_id").asUUID();
if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), folder_id) != mRecursiveCatUUIDs.end())
{
recursive_cats.push_back(fodler_id);
recursive_cats.push_back(folder_id);
}
if (folders.size() == (S32)(size / 2))
{

View File

@ -47,9 +47,11 @@ class LLInventoryModelBackgroundFetch : public LLSingleton<LLInventoryModelBackg
~LLInventoryModelBackgroundFetch();
public:
// Start and stop background breadth-first fetching of inventory contents.
// Start background breadth-first fetching of inventory contents.
// This gets triggered when performing a filter-search.
void start(const LLUUID& cat_id = LLUUID::null, BOOL recursive = TRUE);
void start(const LLUUID& cat_id = LLUUID::null, bool recursive = true);
void scheduleFolderFetch(const LLUUID& cat_id, bool forced = false);
void scheduleItemFetch(const LLUUID& item_id, bool forced = false);
BOOL folderFetchActive() const;
bool isEverythingFetched() const; // completing the fetch once per session should be sufficient
@ -62,16 +64,47 @@ public:
bool inventoryFetchCompleted() const;
bool inventoryFetchInProgress() const;
void findLostItems();
void incrFetchCount(S32 fetching);
void findLostItems();
void incrFetchCount(S32 fetching);
void incrFetchFolderCount(S32 fetching);
bool isBulkFetchProcessingComplete() const;
bool isFolderFetchProcessingComplete() const;
void setAllFoldersFetched();
void addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category);
void addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category);
typedef boost::function<void()> folders_fetched_callback_t;
boost::signals2::connection setFetchCompletionCallback(folders_fetched_callback_t cb);
void addRequestAtFront(const LLUUID & id, bool recursive, bool is_category);
void addRequestAtBack(const LLUUID & id, bool recursive, bool is_category);
protected:
typedef enum {
FT_DEFAULT = 0,
FT_FORCED, // request non-recursively even if already loaded
FT_CONTENT_RECURSIVE, // request content recursively
FT_FOLDER_AND_CONTENT, // request folder, then content recursively
FT_RECURSIVE, // request everything recursively
} EFetchType;
struct FetchQueueInfo
{
FetchQueueInfo(const LLUUID& id, EFetchType recursive, bool is_category = true)
: mUUID(id),
mIsCategory(is_category),
mFetchType(recursive)
{}
LLUUID mUUID;
bool mIsCategory;
EFetchType mFetchType;
};
typedef std::deque<FetchQueueInfo> fetch_queue_t;
void onAISContentCalback(const LLUUID& request_id, const uuid_vec_t &content_ids, const LLUUID& response_id, EFetchType fetch_type);
void onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type);
void bulkFetchViaAis();
void bulkFetchViaAis(const FetchQueueInfo& fetch_info);
void bulkFetch();
void backgroundFetch();
@ -80,31 +113,23 @@ protected:
bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const;
private:
BOOL mRecursiveInventoryFetchStarted;
BOOL mRecursiveLibraryFetchStarted;
BOOL mAllFoldersFetched;
bool mRecursiveInventoryFetchStarted;
bool mRecursiveLibraryFetchStarted;
bool mAllRecursiveFoldersFetched;
typedef boost::signals2::signal<void()> folders_fetched_signal_t;
folders_fetched_signal_t mFoldersFetchedSignal;
BOOL mBackgroundFetchActive;
bool mBackgroundFetchActive;
bool mFolderFetchActive;
S32 mFetchCount;
S32 mLastFetchCount; // for debug
S32 mFetchFolderCount;
LLFrameTimer mFetchTimer;
F32 mMinTimeBetweenFetches;
struct FetchQueueInfo
{
FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true)
: mUUID(id),
mIsCategory(is_category),
mRecursive(recursive)
{}
LLUUID mUUID;
bool mIsCategory;
BOOL mRecursive;
};
typedef std::deque<FetchQueueInfo> fetch_queue_t;
fetch_queue_t mFetchQueue;
fetch_queue_t mFetchFolderQueue;
fetch_queue_t mFetchItemQueue;
std::list<LLUUID> mExpectedFolderIds; // for debug, should this track time?
};
#endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H

View File

@ -37,8 +37,10 @@
#include "llagent.h"
#include "llagentwearables.h"
#include "llaisapi.h"
#include "llfloater.h"
#include "llfocusmgr.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
@ -56,6 +58,7 @@
#include "llsdutil.h"
#include <deque>
const S32 LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS = 7;
const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 60.0f;
@ -149,7 +152,7 @@ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& i
void LLInventoryFetchItemsObserver::changed(U32 mask)
{
LL_DEBUGS() << this << " remaining incomplete " << mIncomplete.size()
LL_DEBUGS("InventoryFetch") << this << " remaining incomplete " << mIncomplete.size()
<< " complete " << mComplete.size()
<< " wait period " << mFetchingPeriod.getRemainingTimeF32()
<< LL_ENDL;
@ -158,6 +161,15 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
// appropriate.
if (!mIncomplete.empty())
{
if (!LLInventoryModelBackgroundFetch::getInstance()->isEverythingFetched())
{
// Folders have a priority over items and they download items as well
// Wait untill initial folder fetch is done
LL_DEBUGS("InventoryFetch") << "Folder fetch in progress, resetting fetch timer" << LL_ENDL;
mFetchingPeriod.reset();
mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY);
}
// Have we exceeded max wait time?
bool timeout_expired = mFetchingPeriod.hasExpired();
@ -176,7 +188,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
if (timeout_expired)
{
// Just concede that this item hasn't arrived in reasonable time and continue on.
LL_WARNS() << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;
LL_WARNS("InventoryFetch") << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;
it = mIncomplete.erase(it);
}
else
@ -191,7 +203,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
if (mIncomplete.empty())
{
LL_DEBUGS() << this << " done at remaining incomplete "
LL_DEBUGS("InventoryFetch") << this << " done at remaining incomplete "
<< mIncomplete.size() << " complete " << mComplete.size() << LL_ENDL;
done();
}
@ -251,29 +263,21 @@ void fetch_items_from_llsd(const LLSD& items_llsd)
void LLInventoryFetchItemsObserver::startFetch()
{
LLUUID owner_id;
bool aisv3 = AISAPI::isAvailable();
LLSD items_llsd;
typedef std::map<LLUUID, uuid_vec_t> requests_by_folders_t;
requests_by_folders_t requests;
for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it)
{
LLViewerInventoryItem* item = gInventory.getItem(*it);
if (item)
{
if (item->isFinished())
{
// It's complete, so put it on the complete container.
mComplete.push_back(*it);
continue;
}
else
{
owner_id = item->getPermissions().getOwner();
}
}
else
{
// assume it's agent inventory.
owner_id = gAgent.getID();
}
LLViewerInventoryItem* item = gInventory.getItem(*it);
if (item && item->isFinished())
{
// It's complete, so put it on the complete container.
mComplete.push_back(*it);
continue;
}
// Ignore categories since they're not items. We
// could also just add this to mComplete but not sure what the
@ -294,17 +298,98 @@ void LLInventoryFetchItemsObserver::startFetch()
// pack this on the message.
mIncomplete.push_back(*it);
// Prepare the data to fetch
LLSD item_entry;
item_entry["owner_id"] = owner_id;
item_entry["item_id"] = (*it);
items_llsd.append(item_entry);
if (aisv3)
{
if (item)
{
LLUUID parent_id = item->getParentUUID();
requests[parent_id].push_back(*it);
}
else
{
// Can happen for gestures and calling cards if server notified us before they fetched
// Request by id without checking for an item.
LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(*it);
}
}
else
{
// Prepare the data to fetch
LLSD item_entry;
if (item)
{
item_entry["owner_id"] = item->getPermissions().getOwner();
}
else
{
// assume it's agent inventory.
item_entry["owner_id"] = gAgent.getID();
}
item_entry["item_id"] = (*it);
items_llsd.append(item_entry);
}
}
mFetchingPeriod.reset();
mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY);
fetch_items_from_llsd(items_llsd);
if (aisv3)
{
for (requests_by_folders_t::value_type &folder : requests)
{
if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS)
{
// requesting one by one will take a while
// do whole folder
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
}
else
{
LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first);
if (cat)
{
if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
// start fetching whole folder since it's not ready either way
cat->fetch();
}
else if (cat->getViewerDescendentCount() <= folder.second.size()
|| cat->getDescendentCount() <= folder.second.size())
{
// Start fetching whole folder since we need all items
LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true);
}
else
{
// get items one by one
for (LLUUID &item_id : folder.second)
{
LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id);
}
}
}
else
{
// Isn't supposed to happen? We should have all folders
// and if item exists, folder is supposed to exist as well.
llassert(false);
LL_WARNS("Inventory") << "Missing folder: " << folder.first << " fetching items individually" << LL_ENDL;
// get items one by one
for (LLUUID &item_id : folder.second)
{
LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id);
}
}
}
}
}
else
{
fetch_items_from_llsd(items_llsd);
}
}
LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const LLUUID& cat_id) :
@ -649,6 +734,13 @@ void LLInventoryCategoriesObserver::changed(U32 mask)
}
}
const LLUUID thumbnail_id = category->getThumbnailUUID();
if (cat_data.mThumbnailId != thumbnail_id)
{
cat_data.mThumbnailId = thumbnail_id;
cat_changed = true;
}
// If anything has changed above, fire the callback.
if (cat_changed)
cat_data.mCallback();
@ -666,6 +758,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;
S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN;
bool can_be_added = true;
LLUUID thumbnail_id;
LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
// If category could not be retrieved it might mean that
@ -677,6 +770,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
// Inventory category version is used to find out if some changes
// to a category have been made.
version = category->getVersion();
thumbnail_id = category->getThumbnailUUID();
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
@ -701,12 +795,12 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t
{
if(init_name_hash)
{
digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents,item_name_hash)));
digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents,item_name_hash)));
}
else
{
mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents)));
mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents)));
}
}
@ -719,23 +813,25 @@ void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id)
}
LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents)
const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents)
: mCatID(cat_id)
, mCallback(cb)
, mVersion(version)
, mDescendentsCount(num_descendents)
, mThumbnailId(thumbnail_id)
, mIsNameHashInitialized(false)
{
}
LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)
const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)
: mCatID(cat_id)
, mCallback(cb)
, mVersion(version)
, mDescendentsCount(num_descendents)
, mThumbnailId(thumbnail_id)
, mIsNameHashInitialized(true)
, mItemNameHash(name_hash)
{

View File

@ -104,6 +104,9 @@ public:
/*virtual*/ void startFetch();
/*virtual*/ void changed(U32 mask);
// For attempts to group requests if too many items are requested
static const S32 MAX_INDIVIDUAL_ITEM_REQUESTS;
private:
LLTimer mFetchingPeriod;
@ -125,7 +128,7 @@ public:
LLInventoryFetchDescendentsObserver(const LLUUID& cat_id = LLUUID::null);
LLInventoryFetchDescendentsObserver(const uuid_vec_t& cat_ids);
/*virtual*/ void startFetch();
virtual void startFetch();
/*virtual*/ void changed(U32 mask);
protected:
BOOL isCategoryComplete(const LLViewerInventoryCategory* cat) const;
@ -273,14 +276,15 @@ protected:
typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
struct LLCategoryData
{
LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents);
LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);
LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents);
LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);
callback_t mCallback;
S32 mVersion;
S32 mDescendentsCount;
digest_t mItemNameHash;
bool mIsNameHashInitialized;
LLUUID mCatID;
LLUUID mThumbnailId;
};
typedef std::map<LLUUID, LLCategoryData> category_map_t;

View File

@ -40,6 +40,7 @@
#include "llfolderviewitem.h"
#include "llfloaterimcontainer.h"
#include "llimview.h"
#include "llinspecttexture.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
@ -160,13 +161,15 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
mInvFVBridgeBuilder(NULL),
mInventoryViewModel(p.name),
mGroupedItemBridge(new LLFolderViewGroupedItemBridge),
mFocusSelection(false)
mFocusSelection(false),
mBuildChildrenViews(true),
mRootInited(false)
{
mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
if (!sColorSetInitialized)
{
sDefaultColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
sDefaultColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);
sDefaultHighlightColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE);
sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
@ -182,6 +185,7 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));
mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this));
mCommitCallbackRegistrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryPanel::fileUploadLocation, this, _2));
mCommitCallbackRegistrar.add("Inventory.OpenNewFolderWindow", boost::bind(&LLInventoryPanel::openSingleViewInventory, this, LLUUID()));
}
LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
@ -248,94 +252,9 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
// save off copy of params
mParams = params;
// Clear up the root view
// Note: This needs to be done *before* we build the new folder view
LLUUID root_id = getRootFolderID();
if (mFolderRoot.get())
{
removeItemID(root_id);
mFolderRoot.get()->destroyView();
}
mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
{
// Determine the root folder in case specified, and
// build the views starting with that folder.
LLFolderView* folder_view = createFolderRoot(root_id);
mFolderRoot = folder_view->getHandle();
addItemID(root_id, mFolderRoot.get());
}
mCommitCallbackRegistrar.popScope();
mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar);
// Scroller
LLRect scroller_view_rect = getRect();
scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
LLScrollContainer::Params scroller_params(mParams.scroll());
scroller_params.rect(scroller_view_rect);
mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
addChild(mScroller);
mScroller->addChild(mFolderRoot.get());
mFolderRoot.get()->setScrollContainer(mScroller);
mFolderRoot.get()->setFollowsAll();
mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
initFolderRoot();
// Set up the callbacks from the inventory we're viewing, and then build everything.
mInventoryObserver = new LLInventoryPanelObserver(this);
mInventory->addObserver(mInventoryObserver);
mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this));
mInventory->addObserver(mCompletionObserver);
if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED)
{
// Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle.
// Initializing views takes a while so always do it onIdle if viewer already loaded.
if (mInventory->isInventoryUsable()
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
{
// Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect
const F64 max_time = 20.f;
initializeViews(max_time);
}
else
{
mViewsInitialized = VIEWS_INITIALIZING;
gIdleCallbacks.addFunction(onIdle, (void*)this);
}
}
if (mSortOrderSetting != INHERIT_SORT_ORDER)
{
setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
}
else
{
setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
}
// hide inbox
if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible"))
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX));
}
// hide marketplace listing box, unless we are a marketplace panel
if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders)
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS));
}
// set the filter for the empty folder if the debug setting is on
if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders"))
{
getFilter().setFilterEmptySystemFolders();
}
// keep track of the clipboard state so that we avoid filtering too much
mClipboardState = LLClipboard::instance().getGeneration();
// Initialize base class params.
LLPanel::initFromParams(mParams);
}
@ -351,13 +270,122 @@ LLInventoryPanel::~LLInventoryPanel()
clearFolderRoot();
}
void LLInventoryPanel::initFolderRoot()
{
// Clear up the root view
// Note: This needs to be done *before* we build the new folder view
LLUUID root_id = getRootFolderID();
if (mFolderRoot.get())
{
removeItemID(root_id);
mFolderRoot.get()->destroyView();
}
mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
{
// Determine the root folder in case specified, and
// build the views starting with that folder.
LLFolderView* folder_view = createFolderRoot(root_id);
mFolderRoot = folder_view->getHandle();
mRootInited = true;
addItemID(root_id, mFolderRoot.get());
}
mCommitCallbackRegistrar.popScope();
mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar);
// Scroller
LLRect scroller_view_rect = getRect();
scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
LLScrollContainer::Params scroller_params(mParams.scroll());
scroller_params.rect(scroller_view_rect);
mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
addChild(mScroller);
mScroller->addChild(mFolderRoot.get());
mFolderRoot.get()->setScrollContainer(mScroller);
mFolderRoot.get()->setFollowsAll();
mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
if (mSelectionCallback)
{
mFolderRoot.get()->setSelectCallback(mSelectionCallback);
}
// Set up the callbacks from the inventory we're viewing, and then build everything.
mInventoryObserver = new LLInventoryPanelObserver(this);
mInventory->addObserver(mInventoryObserver);
mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this));
mInventory->addObserver(mCompletionObserver);
if (mBuildViewsOnInit)
{
initializeViewBuilding();
}
if (mSortOrderSetting != INHERIT_SORT_ORDER)
{
setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
}
else
{
setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
}
// hide inbox
if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible"))
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX));
}
// hide marketplace listing box, unless we are a marketplace panel
if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders)
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS));
}
// set the filter for the empty folder if the debug setting is on
if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders"))
{
getFilter().setFilterEmptySystemFolders();
}
// keep track of the clipboard state so that we avoid filtering too much
mClipboardState = LLClipboard::instance().getGeneration();
}
void LLInventoryPanel::initializeViewBuilding()
{
if (mViewsInitialized == VIEWS_UNINITIALIZED)
{
LL_DEBUGS("Inventory") << "Setting views for " << getName() << " to initialize" << LL_ENDL;
// Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle.
// Initializing views takes a while so always do it onIdle if viewer already loaded.
if (mInventory->isInventoryUsable()
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
{
// Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect
const F64 max_time = 20.f;
initializeViews(max_time);
}
else
{
mViewsInitialized = VIEWS_INITIALIZING;
gIdleCallbacks.addFunction(onIdle, (void*)this);
}
}
}
/*virtual*/
void LLInventoryPanel::onVisibilityChange(BOOL new_visibility)
{
if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED)
{
mViewsInitialized = VIEWS_INITIALIZING;
gIdleCallbacks.addFunction(onIdle, (void*)this);
// first call can be from tab initialization
if (gFloaterView->getParentFloater(this) != NULL)
{
initializeViewBuilding();
}
}
LLPanel::onVisibilityChange(new_visibility);
}
@ -743,7 +771,7 @@ LLUUID LLInventoryPanel::getRootFolderID()
LLStringExplicit label(mParams.start_folder.name());
setLabel(label);
root_id = gInventory.findCategoryUUIDForType(preferred_type, false);
root_id = gInventory.findCategoryUUIDForType(preferred_type);
if (root_id.isNull())
{
LL_WARNS() << "Could not find folder of type " << preferred_type << LL_ENDL;
@ -878,6 +906,7 @@ void LLInventoryPanel::idle(void* user_data)
void LLInventoryPanel::initializeViews(F64 max_time)
{
if (!gInventory.isInventoryUsable()) return;
if (!mRootInited) return;
mViewsInitialized = VIEWS_BUILDING;
@ -905,7 +934,10 @@ void LLInventoryPanel::initializeViews(F64 max_time)
gIdleCallbacks.addFunction(idle, this);
openStartFolderOrMyInventory();
if(mParams.open_first_folder)
{
openStartFolderOrMyInventory();
}
// Special case for new user login
if (gAgent.isFirstLogin())
@ -937,8 +969,8 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br
params.tool_tip = params.name;
params.allow_drop = allow_drop;
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
return LLUICtrlFactory::create<LLFolderViewFolder>(params);
}
@ -954,8 +986,8 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge
params.rect = LLRect (0, 0, 0, 0);
params.tool_tip = params.name;
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor);
params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);
return LLUICtrlFactory::create<LLFolderViewItem>(params);
}
@ -1011,8 +1043,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
LLInventoryObject const* objectp,
LLFolderViewItem *folder_view_item,
LLFolderViewFolder *parent_folder,
const EBuildModes &mode)
const EBuildModes &mode,
S32 depth)
{
depth++;
// Force the creation of an extra root level folder item if required by the inventory panel (default is "false")
bool allow_drop = true;
bool create_root = false;
@ -1042,7 +1077,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
if (objectp->getType() >= LLAssetType::AT_COUNT)
{
// Example: Happens when we add assets of new, not yet supported type to library
LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : "
LL_DEBUGS("Inventory") << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : "
<< ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID()
<< LL_ENDL;
@ -1112,7 +1147,8 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
}
}
bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY;
bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY
&& (mBuildChildrenViews || depth == 0);
if (create_children)
{
@ -1132,12 +1168,15 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
{
create_children = false;
// run it again for the sake of creating children
mBuildViewsQueue.push_back(id);
if (mBuildChildrenViews || depth == 0)
{
mBuildViewsQueue.push_back(id);
}
}
else
{
create_children = true;
folder_view_item->setChildrenInited(true);
folder_view_item->setChildrenInited(mBuildChildrenViews);
}
break;
}
@ -1145,7 +1184,10 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
{
create_children = false;
// run it to create children, current caller is only interested in current view
mBuildViewsQueue.push_back(id);
if (mBuildChildrenViews || depth == 0)
{
mBuildViewsQueue.push_back(id);
}
break;
}
case BUILD_ONE_FOLDER:
@ -1175,7 +1217,13 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
LLViewerInventoryItem::item_array_t* items;
mInventory->lockDirectDescendentArrays(id, categories, items);
// Make sure panel won't lock in a loop over existing items if
// folder is enormous and at least some work gets done
const S32 MIN_ITEMS_PER_CALL = 500;
const S32 starting_item_count = mItemMap.size();
LLFolderViewFolder *parentp = dynamic_cast<LLFolderViewFolder*>(folder_view_item);
bool done = true;
if(categories)
{
@ -1193,11 +1241,28 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
// each time, especially since content is growing, we can just
// iter over copy of mItemMap in some way
LLFolderViewItem* view_itemp = getItemByID(cat->getUUID());
buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode));
buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth);
}
else
{
buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode));
buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth);
}
}
if (!mBuildChildrenViews
&& mode == BUILD_TIMELIMIT
&& MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size())
{
// Single folder view, check if we still have time
//
// Todo: make sure this causes no dupplciates, breaks nothing,
// especially filters and arrange
F64 curent_time = LLTimer::getTotalSeconds();
if (mBuildViewsEndTime < curent_time)
{
mBuildViewsQueue.push_back(id);
done = false;
break;
}
}
}
@ -1217,10 +1282,33 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,
// each time, especially since content is growing, we can just
// iter over copy of mItemMap in some way
LLFolderViewItem* view_itemp = getItemByID(item->getUUID());
buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode);
buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode, depth);
}
if (!mBuildChildrenViews
&& mode == BUILD_TIMELIMIT
&& MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size())
{
// Single folder view, check if we still have time
//
// Todo: make sure this causes no dupplciates, breaks nothing,
// especially filters and arrange
F64 curent_time = LLTimer::getTotalSeconds();
if (mBuildViewsEndTime < curent_time)
{
mBuildViewsQueue.push_back(id);
done = false;
break;
}
}
}
}
if (!mBuildChildrenViews && done)
{
// flat list is done initializing folder
folder_view_item->setChildrenInited(true);
}
mInventory->unlockDirectDescendentArrays(id);
}
@ -1285,6 +1373,37 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
return TRUE;
}
BOOL LLInventoryPanel::handleToolTip(S32 x, S32 y, MASK mask)
{
if (const LLFolderViewItem* hover_item_p = (!mFolderRoot.isDead()) ? mFolderRoot.get()->getHoveredItem() : nullptr)
{
if (const LLFolderViewModelItemInventory* vm_item_p = static_cast<const LLFolderViewModelItemInventory*>(hover_item_p->getViewModelItem()))
{
LLSD params;
params["inv_type"] = vm_item_p->getInventoryType();
params["thumbnail_id"] = vm_item_p->getThumbnailUUID();
params["item_id"] = vm_item_p->getUUID();
// tooltip should only show over folder, but screen
// rect includes items under folder as well
LLRect actionable_rect = hover_item_p->calcScreenRect();
if (hover_item_p->isOpen() && hover_item_p->hasVisibleChildren())
{
actionable_rect.mBottom = actionable_rect.mTop - hover_item_p->getItemHeight();
}
LLToolTipMgr::instance().show(LLToolTip::Params()
.message(hover_item_p->getToolTip())
.sticky_rect(actionable_rect)
.delay_time(LLView::getTooltipTimeout())
.create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1))
.create_params(params));
return TRUE;
}
}
return LLPanel::handleToolTip(x, y, mask);
}
BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
@ -1330,6 +1449,45 @@ void LLInventoryPanel::onFocusReceived()
// inventory now handles cut/copy/paste/delete
LLEditMenuHandler::gEditMenuHandler = mFolderRoot.get();
// Tab support, when tabbing into this view, select first item
// (ideally needs to account for scroll)
bool select_first = mSelectThisID.isNull() && mFolderRoot.get() && mFolderRoot.get()->getSelectedCount() == 0;
if (select_first)
{
LLFolderViewFolder::folders_t::const_iterator folders_it = mFolderRoot.get()->getFoldersBegin();
LLFolderViewFolder::folders_t::const_iterator folders_end = mFolderRoot.get()->getFoldersEnd();
for (; folders_it != folders_end; ++folders_it)
{
const LLFolderViewFolder* folder_view = *folders_it;
if (folder_view->getVisible())
{
const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(folder_view->getViewModelItem());
setSelectionByID(modelp->getUUID(), TRUE);
select_first = false;
break;
}
}
}
if (select_first)
{
LLFolderViewFolder::items_t::const_iterator items_it = mFolderRoot.get()->getItemsBegin();
LLFolderViewFolder::items_t::const_iterator items_end = mFolderRoot.get()->getItemsEnd();
for (; items_it != items_end; ++items_it)
{
const LLFolderViewItem* item_view = *items_it;
if (item_view->getVisible())
{
const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(item_view->getViewModelItem());
setSelectionByID(modelp->getUUID(), TRUE);
break;
}
}
}
LLPanel::onFocusReceived();
}
@ -1380,6 +1538,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::
{
mFolderRoot.get()->setSelectCallback(cb);
}
mSelectionCallback = cb;
}
void LLInventoryPanel::clearSelection()
@ -1426,6 +1585,10 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it
{
fv->startRenamingSelectedItem();
}
else
{
LL_DEBUGS("Inventory") << "Failed to start renemr, no items selected" << LL_ENDL;
}
}
std::set<LLFolderViewItem*> selected_items = mFolderRoot.get()->getSelectionList();
@ -1621,6 +1784,11 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata)
}
}
void LLInventoryPanel::openSingleViewInventory(LLUUID folder_id)
{
LLPanelMainInventory::newFolderWindow(folder_id.isNull() ? LLFolderBridge::sSelf.get()->getUUID() : folder_id);
}
void LLInventoryPanel::purgeSelectedItems()
{
if (!mFolderRoot.get()) return;
@ -1754,22 +1922,49 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open)
}
//static
void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL main_panel, BOOL take_keyboard_focus, BOOL reset_filter)
void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL use_main_panel, BOOL take_keyboard_focus, BOOL reset_filter)
{
LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");
sidepanel_inventory->showInventoryPanel();
bool in_inbox = (gInventory.isObjectDescendentOf(obj_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX)));
if (!in_inbox && (main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected()))
if (!in_inbox && (use_main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected()))
{
sidepanel_inventory->selectAllItemsPanel();
}
LLFloater* inventory_floater = LLFloaterSidePanelContainer::getTopmostInventoryFloater();
if(!auto_open && inventory_floater && inventory_floater->getVisible())
{
LLSidepanelInventory *inventory_panel = inventory_floater->findChild<LLSidepanelInventory>("main_panel");
LLPanelMainInventory* main_panel = inventory_panel->getMainInventoryPanel();
if(main_panel->isSingleFolderMode() && main_panel->isGalleryViewMode())
{
LL_DEBUGS("Inventory") << "Opening gallery panel for item" << obj_id << LL_ENDL;
main_panel->setGallerySelection(obj_id);
return;
}
}
LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
if (main_inventory && main_inventory->isSingleFolderMode()
&& use_main_panel)
{
const LLInventoryObject *obj = gInventory.getObject(obj_id);
if (obj)
{
LL_DEBUGS("Inventory") << "Opening main inventory panel for item" << obj_id << LL_ENDL;
main_inventory->setSingleFolderViewRoot(obj->getParentUUID(), false);
main_inventory->setGallerySelection(obj_id);
return;
}
}
LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open);
if (active_panel)
{
LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL;
LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id << LL_ENDL;
if (reset_filter)
{
@ -1788,7 +1983,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L
inventory_panel->setSelection(obj_id, take_keyboard_focus);
}
}
else
else if (auto_open)
{
LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");
if (floater_inventory)
@ -1797,9 +1992,33 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L
}
active_panel->setSelection(obj_id, take_keyboard_focus);
}
else
{
// Created items are going to receive proper focus from callbacks
active_panel->setSelection(obj_id, take_keyboard_focus);
}
}
}
void LLInventoryPanel::setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id)
{
LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
{
LLFloaterSidePanelContainer* inventory_floater = dynamic_cast<LLFloaterSidePanelContainer*>(*iter);
LLSidepanelInventory* sidepanel_inventory = inventory_floater->findChild<LLSidepanelInventory>("main_panel");
LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
if (main_inventory && panel->hasAncestor(main_inventory) && !main_inventory->isSingleFolderMode())
{
main_inventory->initSingleFolderRoot(folder_id);
main_inventory->toggleViewMode();
main_inventory->setSingleFolderViewRoot(folder_id, false);
}
}
}
void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type)
{
getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type));
@ -2008,10 +2227,205 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params)
mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER;
}
static LLDefaultChildRegistry::Register<LLInventorySingleFolderPanel> t_single_folder_inventory_panel("single_folder_inventory_panel");
LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params)
: LLInventoryPanel(params)
{
mBuildChildrenViews = false;
getFilter().setSingleFolderMode(true);
getFilter().setEmptyLookupMessage("InventorySingleFolderNoMatches");
getFilter().setDefaultEmptyLookupMessage("InventorySingleFolderEmpty");
mCommitCallbackRegistrar.replace("Inventory.DoToSelected", boost::bind(&LLInventorySingleFolderPanel::doToSelected, this, _2));
mCommitCallbackRegistrar.replace("Inventory.DoCreate", boost::bind(&LLInventorySingleFolderPanel::doCreate, this, _2));
mCommitCallbackRegistrar.replace("Inventory.Share", boost::bind(&LLInventorySingleFolderPanel::doShare, this));
}
LLInventorySingleFolderPanel::~LLInventorySingleFolderPanel()
{
}
void LLInventorySingleFolderPanel::initFromParams(const Params& p)
{
mFolderID = gInventory.getRootFolderID();
mParams = p;
LLPanel::initFromParams(mParams);
}
void LLInventorySingleFolderPanel::initFolderRoot(const LLUUID& start_folder_id)
{
if(mRootInited) return;
mRootInited = true;
if(start_folder_id.notNull())
{
mFolderID = start_folder_id;
}
mParams.open_first_folder = false;
mParams.start_folder.id = mFolderID;
LLInventoryPanel::initFolderRoot();
mFolderRoot.get()->setSingleFolderMode(true);
}
void LLInventorySingleFolderPanel::changeFolderRoot(const LLUUID& new_id)
{
if (mFolderID != new_id)
{
if(mFolderID.notNull())
{
mBackwardFolders.push_back(mFolderID);
}
mFolderID = new_id;
updateSingleFolderRoot();
}
}
void LLInventorySingleFolderPanel::onForwardFolder()
{
if(isForwardAvailable())
{
mBackwardFolders.push_back(mFolderID);
mFolderID = mForwardFolders.back();
mForwardFolders.pop_back();
updateSingleFolderRoot();
}
}
void LLInventorySingleFolderPanel::onBackwardFolder()
{
if(isBackwardAvailable())
{
mForwardFolders.push_back(mFolderID);
mFolderID = mBackwardFolders.back();
mBackwardFolders.pop_back();
updateSingleFolderRoot();
}
}
void LLInventorySingleFolderPanel::clearNavigationHistory()
{
mForwardFolders.clear();
mBackwardFolders.clear();
}
bool LLInventorySingleFolderPanel::isBackwardAvailable()
{
return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back()));
}
bool LLInventorySingleFolderPanel::isForwardAvailable()
{
return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back()));
}
boost::signals2::connection LLInventorySingleFolderPanel::setRootChangedCallback(root_changed_callback_t cb)
{
return mRootChangedSignal.connect(cb);
}
void LLInventorySingleFolderPanel::updateSingleFolderRoot()
{
if (mFolderID != getRootFolderID())
{
mRootChangedSignal();
LLUUID root_id = mFolderID;
if (mFolderRoot.get())
{
mItemMap.clear();
mFolderRoot.get()->destroyRoot();
}
mCommitCallbackRegistrar.pushScope();
{
LLFolderView* folder_view = createFolderRoot(root_id);
folder_view->setChildrenInited(false);
mFolderRoot = folder_view->getHandle();
mFolderRoot.get()->setSingleFolderMode(true);
addItemID(root_id, mFolderRoot.get());
LLRect scroller_view_rect = getRect();
scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
LLScrollContainer::Params scroller_params(mParams.scroll());
scroller_params.rect(scroller_view_rect);
if (mScroller)
{
removeChild(mScroller);
delete mScroller;
mScroller = NULL;
}
mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
addChild(mScroller);
mScroller->addChild(mFolderRoot.get());
mFolderRoot.get()->setScrollContainer(mScroller);
mFolderRoot.get()->setFollowsAll();
mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox);
if (!mSelectionCallback.empty())
{
mFolderRoot.get()->setSelectCallback(mSelectionCallback);
}
}
mCommitCallbackRegistrar.popScope();
mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
buildNewViews(mFolderID);
LLFloater* root_floater = gFloaterView->getParentFloater(this);
if(root_floater)
{
root_floater->setFocus(true);
}
}
}
bool LLInventorySingleFolderPanel::hasVisibleItems()
{
return mFolderRoot.get()->hasVisibleChildren();
}
void LLInventorySingleFolderPanel::doCreate(const LLSD& userdata)
{
std::string type_name = userdata.asString();
LLUUID dest_id = LLFolderBridge::sSelf.get()->getUUID();
if (("category" == type_name) || ("outfit" == type_name))
{
changeFolderRoot(dest_id);
}
reset_inventory_filter();
menu_create_inventory_item(this, dest_id, userdata);
}
void LLInventorySingleFolderPanel::doToSelected(const LLSD& userdata)
{
if (("open_in_current_window" == userdata.asString()))
{
changeFolderRoot(LLFolderBridge::sSelf.get()->getUUID());
return;
}
LLInventoryPanel::doToSelected(userdata);
}
void LLInventorySingleFolderPanel::doShare()
{
LLAvatarActions::shareWithAvatars(this);
}
/************************************************************************/
/* Asset Pre-Filtered Inventory Panel related class */
/************************************************************************/
LLAssetFilteredInventoryPanel::LLAssetFilteredInventoryPanel(const Params& p)
: LLInventoryPanel(p)
, mAssetType(LLAssetType::AT_NONE)
{
}
void LLAssetFilteredInventoryPanel::initFromParams(const Params& p)
{
mAssetType = LLAssetType::lookup(p.filter_asset_type.getValue());

View File

@ -107,6 +107,7 @@ public:
Optional<LLFolderView::Params> folder_view;
Optional<LLFolderViewFolder::Params> folder;
Optional<LLFolderViewItem::Params> item;
Optional<bool> open_first_folder;
// All item and folder views will be initialized on init if true (default)
// Will initialize on visibility change otherwise.
@ -126,6 +127,7 @@ public:
show_root_folder("show_root_folder", false),
allow_drop_on_root("allow_drop_on_root", true),
use_marketplace_folders("use_marketplace_folders", false),
open_first_folder("open_first_folder", true),
scroll("scroll"),
accepts_drag_and_drop("accepts_drag_and_drop"),
folder_view("folder_view"),
@ -159,22 +161,23 @@ public:
LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; }
// LLView methods
/*virtual*/ void onVisibilityChange(BOOL new_visibility);
void draw();
/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
BOOL handleHover(S32 x, S32 y, MASK mask);
/*virtual*/ void onVisibilityChange(BOOL new_visibility) override;
void draw() override;
/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ) override;
BOOL handleHover(S32 x, S32 y, MASK mask) override;
/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
std::string& tooltip_msg);
std::string& tooltip_msg) override;
BOOL handleToolTip(S32 x, S32 y, MASK mask) override;
// LLUICtrl methods
/*virtual*/ void onFocusLost();
/*virtual*/ void onFocusReceived();
/*virtual*/ void onFocusLost() override;
/*virtual*/ void onFocusReceived() override;
void onFolderOpening(const LLUUID &id);
// LLBadgeHolder methods
bool addBadge(LLBadge * badge);
bool addBadge(LLBadge * badge) override;
// Call this method to set the selection.
void openAllFolders();
@ -211,6 +214,7 @@ public:
LLUUID getRootFolderID();
LLScrollContainer* getScrollableContainer() { return mScroller; }
bool getAllowDropOnRoot() { return mParams.allow_drop_on_root; }
bool areViewsInitialized() { return mViewsInitialized == VIEWS_INITIALIZED && mFolderRoot.get() && !mFolderRoot.get()->needsArrange(); }
void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);
@ -221,6 +225,7 @@ public:
void doCreate(const LLSD& userdata);
bool beginIMSession();
void fileUploadLocation(const LLSD& userdata);
void openSingleViewInventory(LLUUID folder_id = LLUUID());
void purgeSelectedItems();
bool attachObject(const LLSD& userdata);
static void idle(void* user_data);
@ -241,10 +246,10 @@ public:
static void openInventoryPanelAndSetSelection(BOOL auto_open,
const LLUUID& obj_id,
BOOL main_panel = FALSE,
BOOL use_main_panel = FALSE,
BOOL take_keyboard_focus = TAKE_FOCUS_YES,
BOOL reset_filter = FALSE);
static void setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id);
void addItemID(const LLUUID& id, LLFolderViewItem* itemp);
void removeItemID(const LLUUID& id);
LLFolderViewItem* getItemByID(const LLUUID& id);
@ -262,6 +267,10 @@ public:
static void callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response, const std::vector<LLUUID> inventory_selected);
void changeFolderRoot(const LLUUID& new_id) {};
void initFolderRoot();
void initializeViewBuilding();
protected:
void openStartFolderOrMyInventory(); // open the first level of inventory
void onItemsCompletion(); // called when selected items are complete
@ -298,6 +307,9 @@ protected:
*/
const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder;
bool mBuildChildrenViews; // build root and children
bool mRootInited;
//--------------------------------------------------------------------
// Sorting
@ -357,6 +369,8 @@ protected:
virtual LLFolderView * createFolderRoot(LLUUID root_id );
virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);
virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge);
boost::function<void(const std::deque<LLFolderViewItem*>& items, BOOL user_action)> mSelectionCallback;
private:
// buildViewsTree does not include some checks and is meant
// for recursive use, use buildNewViews() for first call
@ -365,7 +379,8 @@ private:
LLInventoryObject const* objectp,
LLFolderViewItem *target_view,
LLFolderViewFolder *parent_folder_view,
const EBuildModes &mode);
const EBuildModes &mode,
S32 depth = -1);
typedef enum e_views_initialization_state
{
@ -381,6 +396,55 @@ private:
std::deque<LLUUID> mBuildViewsQueue;
};
class LLInventorySingleFolderPanel : public LLInventoryPanel
{
public:
struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params>
{};
void initFromParams(const Params& p);
bool isSelectionRemovable() { return false; }
void initFolderRoot(const LLUUID& start_folder_id = LLUUID::null);
void changeFolderRoot(const LLUUID& new_id);
void onForwardFolder();
void onBackwardFolder();
void clearNavigationHistory();
LLUUID getSingleFolderRoot() { return mFolderID; }
void doCreate(const LLSD& userdata);
void doToSelected(const LLSD& userdata);
void doShare();
bool isBackwardAvailable();
bool isForwardAvailable();
bool hasVisibleItems();
void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; }
void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; }
std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; }
std::list<LLUUID> getNavForwardList() { return mForwardFolders; }
typedef boost::function<void()> root_changed_callback_t;
boost::signals2::connection setRootChangedCallback(root_changed_callback_t cb);
protected:
LLInventorySingleFolderPanel(const Params& params);
~LLInventorySingleFolderPanel();
void updateSingleFolderRoot();
friend class LLUICtrlFactory;
LLUUID mFolderID;
std::list<LLUUID> mBackwardFolders;
std::list<LLUUID> mForwardFolders;
boost::signals2::signal<void()> mRootChangedSignal;
};
/************************************************************************/
/* Asset Pre-Filtered Inventory Panel related class */
/* Exchanges filter's flexibility for speed of generation and */
@ -400,7 +464,7 @@ public:
void initFromParams(const Params& p);
protected:
LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {}
LLAssetFilteredInventoryPanel(const Params& p);
friend class LLUICtrlFactory;
public:
~LLAssetFilteredInventoryPanel() {}

View File

@ -87,6 +87,7 @@ LLLoginInstance::LLLoginInstance() :
mLoginModule(new LLLogin()),
mNotifications(NULL),
mLoginState("offline"),
mSaveMFA(true),
mAttemptComplete(false),
mTransferRate(0.0f),
mDispatcher("LLLoginInstance", "change")
@ -449,10 +450,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
gViewerWindow->setShowProgress(FALSE);
}
LLSD args(llsd::map( "MESSAGE", LLTrans::getString(response["message_id"]) ));
LLSD payload;
LLNotificationsUtil::add("PromptMFAToken", args, payload,
boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
showMFAChallange(LLTrans::getString(response["message_id"]));
}
else if( reason_response == "key"
|| reason_response == "presence"
@ -540,10 +538,7 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
{
// SL-18511 this TOS failure happened while we are in the middle of an MFA challenge/response.
// the previously entered token is very likely expired, so prompt again
LLSD args(llsd::map( "MESSAGE", LLTrans::getString("LoginFailedAuthenticationMFARequired") ));
LLSD payload;
LLNotificationsUtil::add("PromptMFAToken", args, payload,
boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
showMFAChallange(LLTrans::getString("LoginFailedAuthenticationMFARequired"));
}
else
{
@ -561,6 +556,22 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
return true;
}
void LLLoginInstance::showMFAChallange(const std::string& message)
{
LLSD args(llsd::map("MESSAGE", message));
LLSD payload;
if (gSavedSettings.getBOOL("RememberUser"))
{
LLNotificationsUtil::add("PromptMFATokenWithSave", args, payload,
boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
}
else
{
LLNotificationsUtil::add("PromptMFAToken", args, payload,
boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2));
}
}
bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & response)
{
bool continue_clicked = response["continue"].asBoolean();
@ -576,6 +587,7 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon
// Set the request data to true and retry login.
mRequestData["params"]["token"] = token;
mSaveMFA = response.has("ignore") ? response["ignore"].asBoolean() : false;
reconnect();
} else {
LL_INFOS("LLLogin") << "PromptMFAToken: no token, attemptComplete" << LL_ENDL;

View File

@ -56,6 +56,7 @@ public:
bool authSuccess() { return mAttemptComplete && mLoginState == "online"; }
const std::string& getLoginState() { return mLoginState; }
bool saveMFA() const { return mSaveMFA; }
LLSD getResponse(const std::string& key) { return getResponse()[key]; }
LLSD getResponse();
@ -84,6 +85,7 @@ private:
void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response);
bool handleTOSResponse(bool v, const std::string& key);
void showMFAChallange(const std::string& message);
bool handleMFAChallenge(LLSD const & notif, LLSD const & response);
void attemptComplete() { mAttemptComplete = true; } // In the future an event?
@ -95,6 +97,7 @@ private:
LLSD mRequestData;
LLSD mResponseData;
bool mAttemptComplete;
bool mSaveMFA;
F64 mTransferRate;
std::string mSerialNumber;
int mLastExecEvent;

Some files were not shown because too many files have changed in this diff Show More