# Conflicts:
#	indra/newview/app_settings/settings.xml
#	indra/newview/llaisapi.cpp
#	indra/newview/llaisapi.h
#	indra/newview/llappviewer.cpp
#	indra/newview/llinventorymodel.cpp
#	indra/newview/llpanelmaininventory.h
#	indra/newview/llstartup.cpp
#	indra/newview/llviewerinventory.cpp
#	indra/newview/skins/default/xui/en/panel_main_inventory.xml
master
Ansariel 2023-05-03 13:00:22 +02:00
commit f156461048
24 changed files with 726 additions and 278 deletions

View File

@ -1198,11 +1198,7 @@ LLSD LLInventoryCategory::asAISLLSD() const
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);
}
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
return sd;
}
@ -1427,16 +1423,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
{
setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString()));
}
LLUUID thumbnail_uuid;
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);
}
setThumbnailUUID(thumbnail_uuid);
if (cat_data.has(INV_NAME_LABEL))
{
mName = cat_data[INV_NAME_LABEL].asString();

View File

@ -19051,6 +19051,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Backup</key>
<integer>0</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>

View File

@ -1443,8 +1443,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;

View File

@ -48,11 +48,16 @@
//=========================================================================
const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3");
const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3");
const S32 AISAPI::HTTP_TIMEOUT = 180;
std::list<AISAPI::ais_query_item_t> AISAPI::sPostponedQuery;
const S32 MAX_SIMULTANEOUS_COROUTINES = 2048;
// AIS3 allows '*' requests, but in reality those will be cut at some point
// Specify own depth to be able to anticipate it and mark folders as incomplete
const S32 MAX_FOLDER_DEPTH_REQUEST = 50;
//-------------------------------------------------------------------------
/*static*/
bool AISAPI::isAvailable()
@ -463,11 +468,11 @@ void AISAPI::FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type, bool rec
{
// can specify depth=*, but server side is going to cap requests
// and reject everything 'over the top',.
depth = 50;
depth = MAX_FOLDER_DEPTH_REQUEST;
}
else
{
depth = llmax(depth, 50);
depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
}
url += "?depth=" + std::to_string(depth);
@ -516,11 +521,11 @@ void AISAPI::FetchCategoryChildren(const std::string &identifier, bool recursive
{
// can specify depth=*, but server side is going to cap requests
// and reject everything 'over the top',.
depth = 50;
depth = MAX_FOLDER_DEPTH_REQUEST;
}
else
{
depth = llmax(depth, 50);
depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
}
url += "?depth=" + std::to_string(depth);
@ -567,11 +572,11 @@ void AISAPI::FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type, bool r
{
// can specify depth=*, but server side is going to cap requests
// and reject everything 'over the top',.
depth = 50;
depth = MAX_FOLDER_DEPTH_REQUEST;
}
else
{
depth = llmax(depth, 50);
depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
}
url += "?depth=" + std::to_string(depth);
@ -597,6 +602,117 @@ void AISAPI::FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type, bool r
EnqueueAISCommand("FetchCategoryCategories", proc);
}
void AISAPI::FetchCategorySubset(const LLUUID& catId,
const uuid_vec_t specificChildren,
ITEM_TYPE type,
bool recursive,
completion_t callback,
S32 depth)
{
std::string cap = (type == INVENTORY) ? getInvCap() : getLibCap();
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
if (callback)
{
callback(LLUUID::null);
}
return;
}
if (specificChildren.empty())
{
LL_WARNS("Inventory") << "Empty request!" << LL_ENDL;
if (callback)
{
callback(LLUUID::null);
}
return;
}
// category/any_folder_id/children?depth=*&children=child_id1,child_id2,child_id3
std::string url = cap + std::string("/category/") + catId.asString() + "/children";
if (recursive)
{
depth = MAX_FOLDER_DEPTH_REQUEST;
}
else
{
depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST);
}
uuid_vec_t::const_iterator iter = specificChildren.begin();
uuid_vec_t::const_iterator end = specificChildren.end();
url += "?depth=" + std::to_string(depth) + "&children=" + iter->asString();
iter++;
while (iter != end)
{
url += "," + iter->asString();
iter++;
}
const S32 MAX_URL_LENGH = 2000; // RFC documentation specifies a maximum length of 2048
if (url.length() > MAX_URL_LENGH)
{
LL_WARNS("Inventory") << "Request url is too long, url: " << url << LL_ENDL;
}
invokationFn_t getFn = boost::bind(
// Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
//----
// _1 -> httpAdapter
// _2 -> httpRequest
// _3 -> url
// _4 -> body
// _5 -> httpOptions
// _6 -> httpHeaders
(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
// get doesn't use body, can pass additional data
LLSD body;
body["depth"] = depth;
LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
_1, getFn, url, catId, body, callback, FETCHCATEGORYSUBSET));
EnqueueAISCommand("FetchCategorySubset", proc);
}
/*static*/
// Will get COF folder, links in it and items those links point to
void AISAPI::FetchCOF(completion_t callback)
{
std::string cap = getInvCap();
if (cap.empty())
{
LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL;
if (callback)
{
callback(LLUUID::null);
}
return;
}
std::string url = cap + std::string("/category/current/links");
invokationFn_t getFn = boost::bind(
// Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload.
static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>
//----
// _1 -> httpAdapter
// _2 -> httpRequest
// _3 -> url
// _4 -> body
// _5 -> httpOptions
// _6 -> httpHeaders
(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6);
LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro,
_1, getFn, url, LLUUID::null, LLSD(), callback, FETCHCOF));
EnqueueAISCommand("FetchCOF", proc);
}
/*static*/
void AISAPI::FetchOrphans(completion_t callback)
{
@ -681,7 +797,7 @@ void AISAPI::onIdle(void *userdata)
}
/*static*/
void AISAPI::onUpdateReceived(const std::string& context, const LLSD& update, COMMAND_TYPE type, const LLSD& request_body)
void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body)
{
LLTimer timer;
if ( (type == UPDATECATEGORY || type == UPDATEITEM)
@ -689,17 +805,8 @@ void AISAPI::onUpdateReceived(const std::string& context, const LLSD& update, CO
{
dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
}
bool is_fetch = (type == FETCHITEM)
|| (type == FETCHCATEGORYCHILDREN)
|| (type == FETCHCATEGORYCATEGORIES)
|| (type == FETCHORPHANS);
// parse update llsd into stuff to do or parse received items.
S32 depth = 0;
if (is_fetch && request_body.has("depth"))
{
depth = request_body["depth"].asInteger();
}
AISUpdate ais_update(update, is_fetch, depth);
AISUpdate ais_update(update, type, request_body);
ais_update.doUpdate(); // execute the updates in the appropriate order.
LL_DEBUGS("Inventory", "AIS3") << "Elapsed processing: " << timer.getElapsedTimeF32() << LL_ENDL;
}
@ -722,7 +829,7 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest());
LLCore::HttpHeaders::ptr_t httpHeaders;
httpOptions->setTimeout(180);
httpOptions->setTimeout(HTTP_TIMEOUT);
LL_DEBUGS("Inventory") << "Request url: " << url << LL_ENDL;
@ -741,7 +848,7 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
LL_DEBUGS("Inventory") << "Request type: " << (S32)type
<< " \nRequest target: " << targetId
<< " \nElapsed time ince request: " << elapsed_time
<< " \nElapsed time since request: " << elapsed_time
<< " \nstatus: " << status.toULong() << LL_ENDL;
}
else
@ -803,7 +910,7 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
}
LL_DEBUGS("Inventory", "AIS3") << "Result: " << result << LL_ENDL;
onUpdateReceived("AISCommand", result, type, body);
onUpdateReceived(result, type, body);
if (callback && !callback.empty())
{
@ -814,6 +921,8 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
case COPYLIBRARYCATEGORY:
case FETCHCATEGORYCATEGORIES:
case FETCHCATEGORYCHILDREN:
case FETCHCATEGORYSUBSET:
case FETCHCOF:
if (result.has("category_id"))
{
ids.emplace(result["category_id"]);
@ -865,6 +974,8 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
//case COPYLIBRARYCATEGORY:
//case FETCHCATEGORYCATEGORIES:
//case FETCHCATEGORYCHILDREN:
//case FETCHCATEGORYSUBSET:
//case FETCHCOF:
// if (result.has("category_id"))
// {
// id = result["category_id"];
@ -920,10 +1031,24 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
}
//-------------------------------------------------------------------------
AISUpdate::AISUpdate(const LLSD& update, bool fetch, S32 depth)
: mFetch(fetch)
, mFetchDepth(depth)
AISUpdate::AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body)
: mType(type)
{
mFetch = (type == AISAPI::FETCHITEM)
|| (type == AISAPI::FETCHCATEGORYCHILDREN)
|| (type == AISAPI::FETCHCATEGORYCATEGORIES)
|| (type == AISAPI::FETCHCATEGORYSUBSET)
|| (type == AISAPI::FETCHCOF)
|| (type == AISAPI::FETCHORPHANS);
// parse update llsd into stuff to do or parse received items.
mFetchDepth = MAX_FOLDER_DEPTH_REQUEST;
if (mFetch && request_body.has("depth"))
{
mFetchDepth = request_body["depth"].asInteger();
}
mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE);
mTimer.start();
parseUpdate(update);
}
@ -942,6 +1067,16 @@ void AISUpdate::clearParseResults()
mCategoryIds.clear();
}
void AISUpdate::checkTimeout()
{
if (mTimer.hasExpired())
{
llcoro::suspend();
LLCoros::checkStop();
mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE);
}
}
void AISUpdate::parseUpdate(const LLSD& update)
{
clearParseResults();
@ -1031,17 +1166,26 @@ void AISUpdate::parseContent(const LLSD& update)
{
if (update.has("linked_id"))
{
parseLink(update);
parseLink(update, mFetchDepth);
}
else if (update.has("item_id"))
{
parseItem(update);
}
if (update.has("category_id"))
{
parseCategory(update, mFetchDepth);
}
if (mType == AISAPI::FETCHCATEGORYSUBSET)
{
// initial category is incomplete, don't process it,
// go for content instead
if (update.has("_embedded"))
{
parseEmbedded(update["_embedded"], mFetchDepth - 1);
}
}
else if (update.has("category_id"))
{
parseCategory(update, mFetchDepth);
}
else
{
if (update.has("_embedded"))
@ -1097,7 +1241,7 @@ void AISUpdate::parseItem(const LLSD& item_map)
}
}
void AISUpdate::parseLink(const LLSD& link_map)
void AISUpdate::parseLink(const LLSD& link_map, S32 depth)
{
LLUUID item_id = link_map["item_id"].asUUID();
LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem);
@ -1150,6 +1294,11 @@ void AISUpdate::parseLink(const LLSD& link_map)
mCatDescendentDeltas[parent_id]++;
new_link->setComplete(true);
}
if (link_map.has("_embedded"))
{
parseEmbedded(link_map["_embedded"], depth);
}
}
else
{
@ -1294,9 +1443,11 @@ void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embe
void AISUpdate::parseEmbedded(const LLSD& embedded, S32 depth)
{
checkTimeout();
if (embedded.has("links")) // _embedded in a category
{
parseEmbeddedLinks(embedded["links"]);
parseEmbeddedLinks(embedded["links"], depth);
}
if (embedded.has("items")) // _embedded in a category
{
@ -1329,7 +1480,7 @@ void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uui
}
}
void AISUpdate::parseEmbeddedLinks(const LLSD& links)
void AISUpdate::parseEmbeddedLinks(const LLSD& links, S32 depth)
{
for(LLSD::map_const_iterator linkit = links.beginMap(),
linkend = links.endMap();
@ -1343,7 +1494,7 @@ void AISUpdate::parseEmbeddedLinks(const LLSD& links)
}
else
{
parseLink(link_map);
parseLink(link_map, depth);
}
}
}
@ -1414,6 +1565,8 @@ void AISUpdate::parseEmbeddedCategories(const LLSD& categories, S32 depth)
void AISUpdate::doUpdate()
{
checkTimeout();
// Do version/descendant accounting.
for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin();
catit != mCatDescendentDeltas.end(); ++catit)
@ -1455,6 +1608,7 @@ void AISUpdate::doUpdate()
}
// CREATE CATEGORIES
const S32 MAX_UPDATE_BACKLOG = 50; // stall prevention
for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin();
create_it != mCategoriesCreated.end(); ++create_it)
{
@ -1463,6 +1617,13 @@ void AISUpdate::doUpdate()
gInventory.updateCategory(new_category, LLInventoryObserver::CREATE);
LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL;
// fetching can receive massive amount of items and fodlers
if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG)
{
gInventory.notifyObservers();
checkTimeout();
}
}
// UPDATE CATEGORIES
@ -1517,6 +1678,13 @@ void AISUpdate::doUpdate()
// case this is create.
LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL;
gInventory.updateItem(new_item, LLInventoryObserver::CREATE);
// fetching can receive massive amount of items and fodlers
if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG)
{
gInventory.notifyObservers();
checkTimeout();
}
}
// UPDATE ITEMS
@ -1577,6 +1745,8 @@ void AISUpdate::doUpdate()
}
}
checkTimeout();
gInventory.notifyObservers();
}

View File

@ -38,6 +38,7 @@
class AISAPI
{
public:
static const S32 HTTP_TIMEOUT;
typedef enum {
INVENTORY,
LIBRARY
@ -59,10 +60,11 @@ public:
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 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,
@ -76,9 +78,12 @@ private:
FETCHITEM,
FETCHCATEGORYCHILDREN,
FETCHCATEGORYCATEGORIES,
FETCHCATEGORYSUBSET,
FETCHCOF,
FETCHORPHANS,
} COMMAND_TYPE;
private:
static const std::string INVENTORY_CAP_NAME;
static const std::string LIBRARY_CAP_NAME;
@ -87,7 +92,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 std::string& context, const LLSD& update, COMMAND_TYPE type, const LLSD& request_body);
static void onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body);
static std::string getInvCap();
static std::string getLibCap();
@ -103,7 +108,7 @@ private:
class AISUpdate
{
public:
AISUpdate(const LLSD& update, bool fetch, S32 depth);
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);
@ -111,12 +116,12 @@ public:
static void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
// [/SL:KB]
// 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, S32 depth);
void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded);
void parseEmbedded(const LLSD& embedded, S32 depth);
void parseEmbeddedLinks(const LLSD& links);
void parseEmbeddedLinks(const LLSD& links, S32 depth);
void parseEmbeddedItems(const LLSD& items);
void parseEmbeddedCategories(const LLSD& categories, S32 depth);
void parseEmbeddedItem(const LLSD& item);
@ -124,6 +129,12 @@ public:
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;
@ -145,6 +156,8 @@ private:
uuid_list_t mCategoryIds;
bool mFetch;
S32 mFetchDepth;
LLTimer mTimer;
AISAPI::COMMAND_TYPE mType;
};
#endif

View File

@ -5131,18 +5131,45 @@ protected:
nullary_func_t mCallable;
};
void callAfterCOFFetch(nullary_func_t cb)
{
LLUUID cat_id = LLAppearanceMgr::instance().getCOF();
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN && AISAPI::isAvailable())
{
// Assume that we have no relevant cache. Fetch cof, and 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);
}
});
// Mark it so that background fetch won't request it if it didn't already
cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE);
}
else
{
// Assume that cache is present. Process like a normal folder.
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 add_wearable_type_counts(const uuid_vec_t& ids,

View File

@ -367,6 +367,7 @@ 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);
// Wear all items in a uuid vector.

View File

@ -221,7 +221,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
&& !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
@ -229,6 +229,11 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
}
}
if (!checkAgainstFilterThumbnails(folder_id))
{
return false;
}
// Marketplace folder filtering
const U32 filterTypes = mFilterOps.mFilterTypes;
const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE |

View File

@ -128,6 +128,7 @@ public:
bool hasDescendents(const LLUUID& cat_id);
bool hasVisibleItems();
void handleModifiedFilter();
LLScrollContainer* getScrollableContainer() { return mScrollPanel; }
protected:

View File

@ -334,6 +334,10 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU
gInventory.updateItem(item);
gInventory.notifyObservers();
}
else if ("replace_links" == action)
{
LLFloaterReg::showInstance("linkreplace", LLSD(selected_id));
}
}
void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response)
@ -451,8 +455,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
if(!is_link)
{
items.push_back(std::string("thumbnail"));
LLViewerInventoryItem* inv_item = gInventory.getItem(selected_id);
if (inv_item && !inv_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()))
if (!is_agent_inventory)
{
disabled_items.push_back(std::string("thumbnail"));
}
@ -466,6 +469,13 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
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(is_trash)
{
@ -490,7 +500,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
}
}
items.push_back(std::string("Purge Item"));
if (!get_is_category_removable(&gInventory, selected_id))
if (is_folder && !get_is_category_removable(&gInventory, selected_id))
{
disabled_items.push_back(std::string("Purge Item"));
}

View File

@ -2878,12 +2878,6 @@ bool LLInventoryModel::loadSkeleton(
cat->setUUID(folder_id.asUUID());
cat->setParent(parent_id.asUUID());
LLSD thumbnail = (*it)["thumbnail"];
if (thumbnail.isMap())
{
cat->setThumbnailUUID(thumbnail["asset_id"].asUUID());
}
LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
LLSD type_default = (*it)["type_default"];
if(type_default.isDefined())
@ -2975,6 +2969,10 @@ bool LLInventoryModel::loadSkeleton(
else
{
cached_ids.insert(tcat->getUUID());
// At the moment download does not provide a thumbnail
// uuid, use the one from cache
tcat->setThumbnailUUID(cat->getThumbnailUUID());
}
}
@ -5257,7 +5255,6 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
}
else if (count_under_root > 1)
{
// LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; // <FS:Beq/> FIRE-31634 [OPENSIM] Better inventory validation logging
validation_info->mDuplicateRequiredSystemFolders.insert(folder_type);
if (!is_automatic && folder_type != LLFolderType::FT_SETTINGS)
{
@ -5273,6 +5270,8 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
// outfits, trash and other non-automatic folders.
validation_info->mFatalSystemDuplicate++;
fatal_errs++;
// <FS:Beq> FIRE-31634 [OPENSIM] Better inventory validation logging
//LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
}
else
{
@ -5282,6 +5281,8 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const
// Exception: FT_SETTINGS is not automatic, but only deserves a warning.
validation_info->mWarnings["non_fatal_system_duplicate_under_root"]++;
warning_count++;
// <FS:Beq> FIRE-31634 [OPENSIM] Better inventory validation logging
//LL_WARNS("Inventory") << "System folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;
}
}
if (count_elsewhere > 0)

View File

@ -303,7 +303,7 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive)
// 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_CONTENT_RECURSIVE));
mFetchFolderQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), FT_FOLDER_AND_CONTENT));
}
else
{
@ -358,17 +358,7 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive)
void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id, bool forced)
{
if (AISAPI::isAvailable())
{
if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != cat_id)
{
// On AIS make sure root goes to the top and follow up recursive
// fetches, not individual requests
mFetchFolderQueue.push_back(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
}
}
else if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id)
{
// Specific folder requests go to front of queue.
mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT));
@ -630,10 +620,67 @@ void ais_simple_item_callback(const LLUUID& inv_id)
LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1);
}
void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType recursion)
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);
std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin() , mExpectedFolderIds.end(), request_id);
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);
@ -655,32 +702,34 @@ void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_i
if (response_id.isNull()) // Failure
{
LL_DEBUGS(LOG_INV , "AIS3") << "Failure response for folder " << request_id << LL_ENDL;
if (recursion == FT_RECURSIVE)
if (fetch_type == FT_RECURSIVE)
{
// A full recursive request failed.
// Try requesting folder and nested content separately
mBackgroundFetchActive = true;
mFolderFetchActive = true;
mFetchFolderQueue.push_front(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_FOLDER_AND_CONTENT));
}
else if (recursion == FT_CONTENT_RECURSIVE)
else if (fetch_type == FT_FOLDER_AND_CONTENT)
{
LL_WARNS() << "Failed to download folder: " << request_id << " Requesting known content separately" << LL_ENDL;
request_descendants = true;
mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE));
}
}
else
{
if (recursion == FT_CONTENT_RECURSIVE || recursion == FT_RECURSIVE)
if (fetch_type == FT_RECURSIVE)
{
// Got the folder, now recursively request content
// 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;
@ -699,17 +748,18 @@ void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_i
it != categories->end();
++it)
{
mFetchFolderQueue.push_front(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE));
}
if (!mFetchFolderQueue.empty())
{
mBackgroundFetchActive = true;
mFolderFetchActive = true;
gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);
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)
@ -730,7 +780,9 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis()
}
static LLCachedControl<U32> ais_pool(gSavedSettings, "PoolSizeAIS", 20);
const U32 max_concurrent_fetches = llmax(1, ais_pool - 1);
// 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)
{
@ -833,10 +885,111 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));
if (cat)
{
if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion() || fetch_info.mFetchType == FT_FORCED)
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
fetch_info.mFetchType > FT_CONTENT_RECURSIVE
? LLViewerInventoryCategory::FETCH_RECURSIVE
: LLViewerInventoryCategory::FETCH_NORMAL;
// start again if we did a non-recursive fetch before
@ -867,7 +1020,7 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
else
{
// Already fetched, check if anything inside needs fetching
if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)
if (fetch_info.mFetchType == FT_RECURSIVE)
{
LLInventoryModel::cat_array_t * categories(NULL);
LLInventoryModel::item_array_t * items(NULL);
@ -881,7 +1034,7 @@ void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetc
}
}
}
} // else?
} // else try to fetch folder either way?
}
}
else

View File

@ -64,8 +64,8 @@ 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;
@ -82,8 +82,9 @@ protected:
typedef enum {
FT_DEFAULT = 0,
FT_FORCED, // request even if already loaded
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
@ -100,7 +101,8 @@ protected:
};
typedef std::deque<FetchQueueInfo> fetch_queue_t;
void onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType recursion);
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();

View File

@ -979,7 +979,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())
@ -2349,10 +2352,11 @@ void LLInventorySingleFolderPanel::setSelectCallback(const boost::function<void(
void LLInventorySingleFolderPanel::initFromParams(const Params& p)
{
Params fav_params(p);
fav_params.start_folder.id = gInventory.getRootFolderID();
LLInventoryPanel::initFromParams(p);
changeFolderRoot(gInventory.getRootFolderID());
mFolderID = gInventory.getRootFolderID();
Params pane_params(p);
pane_params.open_first_folder = false;
pane_params.start_folder.id = mFolderID;
LLInventoryPanel::initFromParams(pane_params);
}
void LLInventorySingleFolderPanel::openInCurrentWindow(const LLSD& userdata)

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"),

View File

@ -134,6 +134,7 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)
mSingleFolderMode(false),
mForceShowInvLayout(false),
mViewMode(MODE_COMBINATION),
mCombinationShapeDirty(true),
mListViewRootUpdatedConnection(),
mGalleryRootUpdatedConnection(),
mViewMenuButton(nullptr), // <FS:Ansariel> Keep better inventory layout
@ -374,6 +375,8 @@ BOOL LLPanelMainInventory::postBuild()
mInventoryGalleryPanel = getChild<LLInventoryGallery>("gallery_view_inv");
mGalleryRootUpdatedConnection = mInventoryGalleryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::updateTitle, this));
mCombinationScrollPanel = getChild<LLUICtrl>("combination_view_inventory");
mCombinationInventoryPanel = getChild<LLInventorySingleFolderPanel>("comb_single_folder_inv");
LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter();
comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS);
@ -387,6 +390,8 @@ BOOL LLPanelMainInventory::postBuild()
comb_gallery_filter.markDefault();
mCombinationGalleryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, true));
mCombinationScroller = getChild<LLView>("combination_scroller");
initListCommandsHandlers();
const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost());
const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost());
@ -402,6 +407,7 @@ BOOL LLPanelMainInventory::postBuild()
// Trigger callback for focus received so we can deselect items in inbox/outbox
LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLPanelMainInventory::onFocusReceived, this));
mCombinationShapeDirty = true;
return TRUE;
}
@ -590,7 +596,7 @@ void LLPanelMainInventory::newFolderWindow(LLUUID folder_id, LLUUID item_to_sele
{
LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();
if (main_inventory && main_inventory->isSingleFolderMode()
&& (main_inventory->getSingleFolderViewRoot() == folder_id))
&& (main_inventory->getCurrentSFVRoot() == folder_id))
{
main_inventory->setFocus(true);
if(item_to_select.notNull())
@ -1251,9 +1257,18 @@ BOOL LLPanelMainInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
}
// virtual
void LLPanelMainInventory::changed(U32)
void LLPanelMainInventory::changed(U32 mask)
{
updateItemcountText();
if ((mask & LLInventoryObserver::REBUILD)
|| (mask & LLInventoryObserver::STRUCTURE)
|| (mask & LLInventoryObserver::REMOVE)
|| (mask & LLInventoryObserver::ADD)
|| (mask & LLInventoryObserver::LABEL))
{
// todo: can be limited to just observed folder
mCombinationShapeDirty = true;
}
}
void LLPanelMainInventory::setFocusFilterEditor()
@ -1264,6 +1279,13 @@ void LLPanelMainInventory::setFocusFilterEditor()
}
}
void LLPanelMainInventory::reshape(S32 width, S32 height, BOOL called_from_parent)
{
mCombinationShapeDirty = true;
LLPanel::reshape(width, height, called_from_parent);
}
// virtual
void LLPanelMainInventory::draw()
{
@ -1293,6 +1315,7 @@ void LLPanelMainInventory::draw()
mActivePanel->setSortOrder(order);
mResortActivePanel = false;
}
LLPanel::draw();
updateItemcountText();
updateCombinationVisibility();
@ -1982,9 +2005,9 @@ void LLPanelMainInventory::toggleViewMode()
getChild<LLPanel>("default_inventory_panel")->setVisible(!mSingleFolderMode);
getChild<LLPanel>("single_folder_inventory")->setVisible(mSingleFolderMode && isListViewMode());
getChild<LLPanel>("gallery_view_inventory")->setVisible(mSingleFolderMode && isGalleryViewMode());
getChild<LLPanel>("combination_view_inventory")->setVisible(mSingleFolderMode && isCombinationViewMode());
getChild<LLLayoutPanel>("nav_buttons")->setVisible(mSingleFolderMode);
getChild<LLButton>("view_mode_btn")->setImageOverlay(mSingleFolderMode ? getString("default_mode_btn") : getString("single_folder_mode_btn"));
mCombinationScrollPanel->setVisible(mSingleFolderMode && isCombinationViewMode());
// <FS:Ansariel> Disable Expand/Collapse buttons in single folder mode
getChild<LLLayoutPanel>("collapse_expand_buttons")->setVisible(!mSingleFolderMode);
@ -2446,6 +2469,7 @@ void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility )
}
// </FS:Ansariel>
}
mCombinationShapeDirty = true;
}
bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata)
@ -2911,27 +2935,57 @@ void LLPanelMainInventory::onCombinationRootChanged(bool gallery_clicked)
}
mForceShowInvLayout = false;
updateTitle();
//force update scroll container
mCombinationShapeDirty = true;
}
void LLPanelMainInventory::updateCombinationVisibility()
{
if(mSingleFolderMode && isCombinationViewMode())
if(mSingleFolderMode && isCombinationViewMode() && mCombinationShapeDirty)
{
bool is_gallery_empty = !mCombinationGalleryPanel->hasVisibleItems();
bool show_inv_pane = mCombinationInventoryPanel->hasVisibleItems() || is_gallery_empty || mForceShowInvLayout;
getChild<LLLayoutPanel>("comb_gallery_layout")->setVisible(!is_gallery_empty);
getChild<LLLayoutPanel>("comb_inventory_layout")->setVisible(show_inv_pane);
mCombinationInventoryPanel->getRootFolder()->setForceArrange(!show_inv_pane);
if(mCombinationInventoryPanel->hasVisibleItems())
{
mForceShowInvLayout = false;
}
if(is_gallery_empty)
mCombinationShapeDirty = false;
mCombinationInventoryPanel->reshape(1,1); // HACK: force reduce visible area
if (!mCombinationGalleryPanel->hasVisibleItems())
{
mCombinationGalleryPanel->handleModifiedFilter();
}
getActivePanel()->getRootFolder();
LLRect inv_rect = mCombinationInventoryPanel->getRect();
LLRect inv_inner_rect = mCombinationInventoryPanel->getScrollableContainer()->getScrolledViewRect();
LLRect galery_rect = mCombinationGalleryPanel->getRect();
LLRect inner_galery_rect = mCombinationGalleryPanel->getScrollableContainer()->getScrolledViewRect();
LLScrollContainer* scroll = static_cast<LLScrollContainer*>(mCombinationScrollPanel);
LLRect scroller_window_rect = scroll->getContentWindowRect();
S32 desired_width = llmax(inv_inner_rect.getWidth(), scroller_window_rect.getWidth());
const S32 BORDER_PAD = 2; // two sides
inv_rect.mBottom = 0;
inv_rect.mRight = inv_rect.mLeft + desired_width;
if (!mCombinationGalleryPanel->hasVisibleItems() || mCombinationInventoryPanel->hasVisibleItems())
{
inv_rect.mTop = inv_rect.mBottom + inv_inner_rect.getHeight() + BORDER_PAD;
}
else
{
inv_rect.mTop = inv_rect.mBottom;
}
galery_rect.mBottom = inv_rect.mTop;
galery_rect.mRight = galery_rect.mLeft + scroller_window_rect.getWidth();
if (mCombinationGalleryPanel->hasVisibleItems())
{
mCombinationGalleryPanel->setVisible(true);
galery_rect.mTop = galery_rect.mBottom + inner_galery_rect.getHeight() + BORDER_PAD;
}
else
{
mCombinationGalleryPanel->setVisible(false);
galery_rect.mTop = galery_rect.mBottom;
}
mCombinationScroller->reshape(desired_width, inv_rect.getHeight() + galery_rect.getHeight(), true);
mCombinationGalleryPanel->setShape(galery_rect, false);
mCombinationInventoryPanel->setShape(inv_rect, false);
}
}
@ -2996,7 +3050,7 @@ void LLPanelMainInventory::setViewMode(EViewModeType mode)
getChild<LLPanel>("single_folder_inventory")->setVisible(mSingleFolderMode && isListViewMode());
getChild<LLPanel>("gallery_view_inventory")->setVisible(mSingleFolderMode && isGalleryViewMode());
getChild<LLPanel>("combination_view_inventory")->setVisible(mSingleFolderMode && isCombinationViewMode());
mCombinationScrollPanel->setVisible(mSingleFolderMode && isCombinationViewMode());
if(isListViewMode())
{

View File

@ -85,8 +85,9 @@ public:
EAcceptance* accept,
std::string& tooltip_msg);
/*virtual*/ void changed(U32);
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
/*virtual*/ void draw();
/*virtual*/ void onVisibilityChange ( BOOL new_visibility );
/*virtual*/ void onVisibilityChange ( BOOL new_visibility );
// <FS:Ansariel> CTRL-F focusses local search editor
/*virtual*/ bool hasAccelerators() const { return true; }
@ -160,8 +161,6 @@ protected:
static BOOL filtersVisible(void* user_data);
void onClearSearch();
static void onFoldersByName(void *user_data);
static BOOL checkFoldersByName(void *user_data);
static BOOL incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
void onFilterSelected();
@ -220,8 +219,10 @@ private:
LLInventorySingleFolderPanel* mSingleFolderPanelInventory;
LLInventoryGallery* mInventoryGalleryPanel;
LLUICtrl* mCombinationScrollPanel;
LLInventorySingleFolderPanel* mCombinationInventoryPanel;
LLInventoryGallery* mCombinationGalleryPanel;
LLView* mCombinationScroller;
// <FS:Zi> Filter dropdown
LLComboBox* mFilterComboBox;
@ -295,6 +296,7 @@ private:
bool mNeedUploadCost;
bool mForceShowInvLayout;
bool mCombinationShapeDirty;
// List Commands //
////////////////////////////////////////////////////////////////////////////////
};

View File

@ -64,10 +64,13 @@ LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params
: LLInventoryPanel(p)
{
LLInboxNewItemsStorage::getInstance()->load();
LLInboxNewItemsStorage::getInstance()->addInboxPanel(this);
}
LLInboxInventoryPanel::~LLInboxInventoryPanel()
{}
{
LLInboxNewItemsStorage::getInstance()->removeInboxPanel(this);
}
void LLInboxInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
@ -128,6 +131,21 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b
return LLUICtrlFactory::create<LLInboxFolderViewItem>(params);
}
void LLInboxInventoryPanel::onRemoveItemFreshness(const LLUUID& item_id)
{
LLInboxFolderViewFolder* inbox_folder_view = dynamic_cast<LLInboxFolderViewFolder*>(getFolderByID(item_id));
if(inbox_folder_view)
{
inbox_folder_view->setFresh(false);
}
LLInboxFolderViewItem* inbox_item_view = dynamic_cast<LLInboxFolderViewItem*>(getItemByID(item_id));
if(inbox_item_view)
{
inbox_item_view->setFresh(false);
}
}
//
// LLInboxFolderViewFolder Implementation
//
@ -364,4 +382,18 @@ void LLInboxNewItemsStorage::load()
}
}
}
void LLInboxNewItemsStorage::removeItem(const LLUUID& id)
{
mNewItemsIDs.erase(id);
//notify inbox panels
for (auto inbox : mInboxPanels)
{
if(inbox)
{
inbox->onRemoveItemFreshness(id);
}
}
}
// eof

View File

@ -49,6 +49,8 @@ public:
void initFromParams(const LLInventoryPanel::Params&);
LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);
LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge);
void onRemoveItemFreshness(const LLUUID& item_id);
};
@ -77,6 +79,7 @@ public:
void deFreshify();
bool isFresh() const { return mFresh; }
void setFresh(bool is_fresh) { mFresh = is_fresh; }
protected:
bool mFresh;
@ -108,6 +111,7 @@ public:
void deFreshify();
bool isFresh() const { return mFresh; }
void setFresh(bool is_fresh) { mFresh = is_fresh; }
protected:
bool mFresh;
@ -125,11 +129,16 @@ public:
void load();
void addFreshItem(const LLUUID& id) { mNewItemsIDs.insert(id); }
void removeItem(const LLUUID& id) { mNewItemsIDs.erase(id); }
void removeItem(const LLUUID& id);
bool isItemFresh(const LLUUID& id) { return (mNewItemsIDs.find(id) != mNewItemsIDs.end()); }
void addInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.insert(inbox); }
void removeInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.erase(inbox); }
private:
std::set<LLUUID> mNewItemsIDs;
std::set<LLInboxInventoryPanel*> mInboxPanels;
};
#endif //LL_INBOXINVENTORYPANEL_H

View File

@ -2985,7 +2985,7 @@ bool idle_startup()
gAgentWearables.sendDummyAgentWearablesUpdate();
}
// </FS:Ansariel> [Legacy Bake]
callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance);
callAfterCOFFetch(set_flags_and_update_appearance);
}
display_startup();

View File

@ -385,9 +385,9 @@ bool LLToolBarView::loadToolbars(bool force_default)
}
// SL-18581: Don't show the starter avatar toolbar button for NUX users
LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
if (gAgent.isFirstLogin())
{
LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
LL_WARNS() << "First login: checking for NUX user." << LL_ENDL;
if (my_outfits_cat != NULL && my_outfits_cat->getDescendentCount() > 0)
{

View File

@ -780,9 +780,16 @@ void LLViewerInventoryCategory::setFetching(LLViewerInventoryCategory::EFetchTyp
{
if (mDescendentsRequested.hasExpired() || (mFetching == FETCH_NONE))
{
const F32 FETCH_TIMER_EXPIRY = 30.0f;
mDescendentsRequested.reset();
mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
if (AISAPI::isAvailable())
{
mDescendentsRequested.setTimerExpirySec(AISAPI::HTTP_TIMEOUT);
}
else
{
const F32 FETCH_TIMER_EXPIRY = 30.0f;
mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
}
}
mFetching = fetching;
}
@ -1600,19 +1607,18 @@ void update_inventory_category(
return;
}
LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj);
new_cat->fromLLSD(updates);
// <FS:Ansariel> [UDP-Msg]
if (AISAPI::isAvailable())
{
// </FS:Ansariel> [UDP-Msg]
LLSD new_llsd = new_cat->asLLSD();
AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1);
AISAPI::UpdateCategory(cat_id, new_llsd, cr);
AISAPI::UpdateCategory(cat_id, updates, cr);
// <FS:Ansariel> [UDP-Msg]
}
else
{
LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj);
new_cat->fromLLSD(updates);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_UpdateInventoryFolder);
msg->nextBlockFast(_PREHASH_AgentData);

View File

@ -900,7 +900,7 @@
follows="all"
layout="topleft" />
</panel>
<panel
<scroll_container
follows="all"
halign="center"
height="319"
@ -910,74 +910,48 @@
top_delta="0"
visible="false"
width="326">
<layout_stack
follows="all"
height="319"
width="326"
animate="false"
drag_handle_gap="13"
drag_handle_thickness="6"
drag_handle_first_indent="18"
drag_handle_second_indent="18"
drag_handle_color="PanelGray"
drag_handle_shift="5"
show_drag_handle="true"
top="0"
left="0"
orientation="vertical">
<layout_panel
border="false"
bevel_style="in"
user_resize="true"
auto_resize="true"
height="209"
width="326"
min_width="150"
name="comb_gallery_layout">
<panel
class="inventory_gallery"
filename="panel_inventory_gallery.xml"
left="0"
top_pad="0"
height="209"
width="326"
name="comb_gallery_view_inv"
background_visible="true"
follows="all"
layout="topleft">
<panel
name="combination_scroller"
follows="all"
layout="topleft"
left="0"
top="0"
height="20"
width="304">
<panel
class="inventory_gallery"
filename="panel_inventory_gallery.xml"
follows="left|right"
layout="topleft"
left="0"
top="0"
height="10"
width="303"
name="comb_gallery_view_inv"
background_visible="true">
</panel>
<single_folder_inventory_panel
name="comb_single_folder_inv"
follows="left|right"
layout="topleft"
left="0"
top="200"
height="10"
width="303"
show_item_link_overlays="true"
background_visible="true"
border="false"
bevel_style="none"
scroll.reserve_scroll_corner="false">
<item
single_folder_mode="true"
folder_indentation="-8"/>
<folder
single_folder_mode="true"
folder_indentation="-8"/>
</single_folder_inventory_panel>
</panel>
</layout_panel>
<layout_panel
border="false"
bevel_style="in"
user_resize="true"
auto_resize="true"
height="110"
width="326"
name="comb_inventory_layout">
<single_folder_inventory_panel
name="comb_single_folder_inv"
follows="all"
left="0"
top="1"
height="110"
width="326"
layout="topleft"
show_item_link_overlays="true"
background_visible="true"
border="false"
bevel_style="none"
scroll.reserve_scroll_corner="false">
<item
single_folder_mode="true"
folder_indentation="-8"/>
<folder
single_folder_mode="true"
folder_indentation="-8"/>
</single_folder_inventory_panel>
</layout_panel>
</layout_stack>
</panel>
</scroll_container>
<panel
follows="left|right|bottom"
height="25"

View File

@ -324,7 +324,7 @@
follows="all"
layout="topleft" />
</panel>
<panel
<scroll_container
follows="all"
halign="center"
height="338"
@ -334,74 +334,48 @@
top_delta="0"
visible="false"
width="322">
<layout_stack
follows="all"
height="338"
width="322"
animate="false"
drag_handle_gap="13"
drag_handle_thickness="6"
drag_handle_first_indent="18"
drag_handle_second_indent="18"
drag_handle_color="PanelGray"
drag_handle_shift="5"
show_drag_handle="true"
top="0"
left="0"
orientation="vertical">
<layout_panel
border="false"
bevel_style="in"
user_resize="true"
auto_resize="true"
height="228"
width="322"
min_width="150"
name="comb_gallery_layout">
<panel
class="inventory_gallery"
filename="panel_inventory_gallery.xml"
left="0"
top_pad="0"
height="228"
width="322"
name="comb_gallery_view_inv"
background_visible="true"
follows="all"
layout="topleft">
<panel
name="combination_scroller"
follows="all"
layout="topleft"
left="0"
top="0"
height="20"
width="300">
<panel
class="inventory_gallery"
filename="panel_inventory_gallery.xml"
follows="left|right"
layout="topleft"
left="0"
top="0"
height="10"
width="299"
name="comb_gallery_view_inv"
background_visible="true">
</panel>
<single_folder_inventory_panel
name="comb_single_folder_inv"
follows="left|right"
layout="topleft"
left="0"
top="200"
height="10"
width="299"
show_item_link_overlays="true"
background_visible="true"
border="false"
bevel_style="none"
scroll.reserve_scroll_corner="false">
<item
single_folder_mode="true"
folder_indentation="-8"/>
<folder
single_folder_mode="true"
folder_indentation="-8"/>
</single_folder_inventory_panel>
</panel>
</layout_panel>
<layout_panel
border="false"
bevel_style="in"
user_resize="true"
auto_resize="true"
height="110"
width="322"
name="comb_inventory_layout">
<single_folder_inventory_panel
name="comb_single_folder_inv"
follows="all"
left="0"
top="1"
height="110"
width="322"
layout="topleft"
show_item_link_overlays="true"
background_visible="true"
border="false"
bevel_style="none"
scroll.reserve_scroll_corner="false">
<item
single_folder_mode="true"
folder_indentation="-8"/>
<folder
single_folder_mode="true"
folder_indentation="-8"/>
</single_folder_inventory_panel>
</layout_panel>
</layout_stack>
</panel>
</scroll_container>
<panel
follows="left|right|bottom"
height="25"