From 9f10d9510d85daffe7e203b5d385e5861675c8ab Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 25 Jun 2015 20:16:30 -0500 Subject: [PATCH] Initial AISv3 merge. New HTTP messages not plugged in yet. --- indra/llappearance/llwearabledata.cpp | 84 +- indra/llappearance/llwearabledata.h | 14 +- indra/llinventory/llinventory.cpp | 84 +- indra/llinventory/llinventory.h | 8 +- indra/llinventory/llpermissions.cpp | 2 +- indra/llinventory/llpermissions.h | 2 +- indra/llinventory/llsaleinfo.cpp | 2 +- indra/llinventory/llsaleinfo.h | 2 +- indra/llmessage/aicurlperservice.h | 2 +- indra/llmessage/aihttptimeoutpolicy.cpp | 7 +- indra/llmessage/llhttpclient.cpp | 10 +- indra/llmessage/llhttpclient.h | 8 +- indra/llui/llmemberlistener.h | 19 + indra/newview/CMakeLists.txt | 2 + indra/newview/character/avatar_lad.xml | 8 +- indra/newview/llagentwearables.cpp | 330 ++- indra/newview/llagentwearables.h | 30 +- indra/newview/llagentwearablesfetch.cpp | 14 +- indra/newview/llappearancemgr.cpp | 2140 ++++++++++------- indra/newview/llappearancemgr.h | 147 +- indra/newview/llappviewer.cpp | 2 + indra/newview/llassetuploadqueue.cpp | 10 +- indra/newview/llassetuploadqueue.h | 4 +- indra/newview/llcompilequeue.cpp | 4 +- indra/newview/llfloatercustomize.cpp | 10 +- indra/newview/llfloaterlandmark.cpp | 9 - indra/newview/llfloateropenobject.cpp | 29 +- indra/newview/llfloateropenobject.h | 5 +- indra/newview/llfolderview.cpp | 13 +- indra/newview/llfolderview.h | 11 +- indra/newview/llfolderviewitem.h | 10 +- indra/newview/llhttpretrypolicy.cpp | 155 ++ indra/newview/llhttpretrypolicy.h | 88 + indra/newview/llinventoryactions.cpp | 224 +- indra/newview/llinventorybridge.cpp | 773 +++--- indra/newview/llinventorybridge.h | 41 +- indra/newview/llinventoryfunctions.cpp | 16 +- indra/newview/llinventoryfunctions.h | 7 + indra/newview/llinventorymodel.cpp | 1485 ++++++++---- indra/newview/llinventorymodel.h | 113 +- .../llinventorymodelbackgroundfetch.cpp | 634 +++-- .../newview/llinventorymodelbackgroundfetch.h | 22 +- indra/newview/llinventoryobserver.cpp | 179 +- indra/newview/llinventoryobserver.h | 66 +- indra/newview/llinventorypanel.cpp | 8 +- indra/newview/llinventorypanel.h | 2 + indra/newview/llpaneleditwearable.cpp | 56 +- indra/newview/llpanelmaininventory.cpp | 5 + indra/newview/llpanelmaininventory.h | 1 + .../llpanelmarketplaceoutboxinventory.cpp | 2 +- indra/newview/llpreview.cpp | 7 - indra/newview/llstartup.cpp | 49 +- indra/newview/lltooldraganddrop.cpp | 2 +- indra/newview/llviewerinventory.cpp | 1057 ++++---- indra/newview/llviewerinventory.h | 92 +- indra/newview/llviewermedia.cpp | 2 +- indra/newview/llviewermessage.cpp | 27 +- indra/newview/llviewerobject.cpp | 19 +- indra/newview/llviewerobject.h | 3 +- indra/newview/llviewerwearable.cpp | 6 +- indra/newview/llviewerwearable.h | 4 +- indra/newview/llvoavatar.cpp | 76 +- indra/newview/llvoavatar.h | 16 +- indra/newview/llvoavatarself.cpp | 78 +- indra/newview/llvoavatarself.h | 12 +- indra/newview/llwebprofile.cpp | 2 +- indra/newview/rlvinventory.cpp | 53 +- indra/newview/rlvinventory.h | 4 +- indra/newview/rlvlocks.cpp | 2 +- indra/newview/rlvlocks.h | 2 +- .../default/xui/en-us/menu_inventory.xml | 2 +- 71 files changed, 4962 insertions(+), 3452 deletions(-) create mode 100644 indra/newview/llhttpretrypolicy.cpp create mode 100644 indra/newview/llhttpretrypolicy.h diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp index 25c0883b5..f81e7eee9 100644 --- a/indra/llappearance/llwearabledata.cpp +++ b/indra/llappearance/llwearabledata.cpp @@ -94,7 +94,7 @@ void LLWearableData::setWearable(const LLWearableType::EType type, U32 index, LL } } -U32 LLWearableData::pushWearable(const LLWearableType::EType type, +void LLWearableData::pushWearable(const LLWearableType::EType type, LLWearable *wearable, bool trigger_updated /* = true */) { @@ -102,38 +102,19 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type, { // no null wearables please! LL_WARNS() << "Null wearable sent for type " << type << LL_ENDL; - return MAX_CLOTHING_PER_TYPE; } -// [RLVa:KB] - Checked: 2010-06-08 (RLVa-1.2.0g) | Added: RLVa-1.2.0g - if ( (type < LLWearableType::WT_COUNT) && (mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE) ) + if (canAddWearable(type)) { - // Don't add the same wearable twice - U32 idxWearable = getWearableIndex(wearable); - llassert(MAX_CLOTHING_PER_TYPE == idxWearable); // pushWearable() on an already added wearable is a bug *somewhere* - if (MAX_CLOTHING_PER_TYPE == idxWearable) - { - mWearableDatas[type].push_back(wearable); - idxWearable = mWearableDatas[type].size() - 1; - } + // [RLVa:KB] - Checked: 2010-06-08 (RLVa-1.2.0g) | Added: RLVa-1.2.0g + if (std::find(mWearableDatas[type].begin(), mWearableDatas[type].end(), wearable) == mWearableDatas[type].end()) + // [/RLVa:KB] + mWearableDatas[type].push_back(wearable); if (trigger_updated) { const BOOL removed = FALSE; wearableUpdated(wearable, removed); } - return idxWearable; } -// [/RLVa:KB] -// if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE) -// { -// mWearableDatas[type].push_back(wearable); -// if (trigger_updated) -// { -// const BOOL removed = FALSE; -// wearableUpdated(wearable, removed); -// } -// return mWearableDatas[type].size()-1; -// } - return MAX_CLOTHING_PER_TYPE; } // virtual @@ -146,7 +127,7 @@ void LLWearableData::wearableUpdated(LLWearable *wearable, BOOL removed) } } -void LLWearableData::popWearable(LLWearable *wearable) +void LLWearableData::eraseWearable(LLWearable *wearable) { if (wearable == NULL) { @@ -154,18 +135,17 @@ void LLWearableData::popWearable(LLWearable *wearable) return; } - U32 index = getWearableIndex(wearable); const LLWearableType::EType type = wearable->getType(); - if (index < MAX_CLOTHING_PER_TYPE && index < getWearableCount(type)) + U32 index; + if (getWearableIndex(wearable,index)) { - popWearable(type, index); + eraseWearable(type, index); } } -void LLWearableData::popWearable(const LLWearableType::EType type, U32 index) +void LLWearableData::eraseWearable(const LLWearableType::EType type, U32 index) { - //llassert_always(index == 0); LLWearable *wearable = getWearable(type, index); if (wearable) { @@ -225,11 +205,11 @@ void LLWearableData::pullCrossWearableValues(const LLWearableType::EType type) } -U32 LLWearableData::getWearableIndex(const LLWearable *wearable) const +BOOL LLWearableData::getWearableIndex(const LLWearable *wearable, U32& index_found) const { if (wearable == NULL) { - return MAX_CLOTHING_PER_TYPE; + return FALSE; } const LLWearableType::EType type = wearable->getType(); @@ -237,18 +217,50 @@ U32 LLWearableData::getWearableIndex(const LLWearable *wearable) const if (wearable_iter == mWearableDatas.end()) { LL_WARNS() << "tried to get wearable index with an invalid type!" << LL_ENDL; - return MAX_CLOTHING_PER_TYPE; + return FALSE; } const wearableentry_vec_t& wearable_vec = wearable_iter->second; for(U32 index = 0; index < wearable_vec.size(); index++) { if (wearable_vec[index] == wearable) { - return index; + index_found = index; + return TRUE; } } - return MAX_CLOTHING_PER_TYPE; + return FALSE; +} + +U32 LLWearableData::getClothingLayerCount() const +{ + U32 count = 0; + for (S32 i = 0; i < LLWearableType::WT_COUNT; i++) + { + LLWearableType::EType type = (LLWearableType::EType)i; + if (LLWearableType::getAssetType(type)==LLAssetType::AT_CLOTHING) + { + count += getWearableCount(type); + } + } + return count; +} + +BOOL LLWearableData::canAddWearable(const LLWearableType::EType type) const +{ + LLAssetType::EType a_type = LLWearableType::getAssetType(type); + if (a_type==LLAssetType::AT_CLOTHING) + { + return (getClothingLayerCount() < MAX_CLOTHING_LAYERS); + } + else if (a_type==LLAssetType::AT_BODYPART) + { + return (getWearableCount(type) < 1); + } + else + { + return FALSE; + } } BOOL LLWearableData::isOnTop(LLWearable* wearable) const diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h index aeb9c1b26..2cf5585e0 100644 --- a/indra/llappearance/llwearabledata.h +++ b/indra/llappearance/llwearabledata.h @@ -61,11 +61,13 @@ public: const LLWearable* getBottomWearable(const LLWearableType::EType type) const; U32 getWearableCount(const LLWearableType::EType type) const; U32 getWearableCount(const U32 tex_index) const; - U32 getWearableIndex(const LLWearable *wearable) const; + BOOL getWearableIndex(const LLWearable *wearable, U32& index) const; + U32 getClothingLayerCount() const; + BOOL canAddWearable(const LLWearableType::EType type) const; BOOL isOnTop(LLWearable* wearable) const; - - static const U32 MAX_CLOTHING_PER_TYPE = 5; + + static const U32 MAX_CLOTHING_LAYERS = 60; //-------------------------------------------------------------------- // Setters @@ -73,11 +75,11 @@ public: protected: // Low-level data structure setter - public access is via setWearableItem, etc. void setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable); - U32 pushWearable(const LLWearableType::EType type, LLWearable *wearable, + void pushWearable(const LLWearableType::EType type, LLWearable *wearable, bool trigger_updated = true); virtual void wearableUpdated(LLWearable *wearable, BOOL removed); - void popWearable(LLWearable *wearable); - void popWearable(const LLWearableType::EType type, U32 index); + void eraseWearable(LLWearable *wearable); + void eraseWearable(const LLWearableType::EType type, U32 index); void clearWearableType(const LLWearableType::EType type); bool swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b); diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 7f76e4478..01c9d1783 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -52,6 +52,7 @@ static const std::string INV_DESC_LABEL("desc"); static const std::string INV_PERMISSIONS_LABEL("permissions"); static const std::string INV_SHADOW_ID_LABEL("shadow_id"); static const std::string INV_ASSET_ID_LABEL("asset_id"); +static const std::string INV_LINKED_ID_LABEL("linked_id"); static const std::string INV_SALE_INFO_LABEL("sale_info"); static const std::string INV_FLAGS_LABEL("flags"); static const std::string INV_CREATION_DATE_LABEL("created_at"); @@ -77,13 +78,15 @@ LLInventoryObject::LLInventoryObject(const LLUUID& uuid, mUUID(uuid), mParentUUID(parent_uuid), mType(type), - mName(name) + mName(name), + mCreationDate(0) { correctInventoryName(mName); } LLInventoryObject::LLInventoryObject() : - mType(LLAssetType::AT_NONE) + mType(LLAssetType::AT_NONE), + mCreationDate(0) { } @@ -249,13 +252,6 @@ BOOL LLInventoryObject::exportLegacyStream(std::ostream& output_stream, BOOL) co return TRUE; } - -void LLInventoryObject::removeFromServer() -{ - // don't do nothin' - LL_WARNS() << "LLInventoryObject::removeFromServer() called. Doesn't do anything." << LL_ENDL; -} - void LLInventoryObject::updateParentOnServer(BOOL) const { // don't do nothin' @@ -277,6 +273,25 @@ void LLInventoryObject::correctInventoryName(std::string& name) LLStringUtil::truncate(name, DB_INV_ITEM_NAME_STR_LEN); } +time_t LLInventoryObject::getCreationDate() const +{ + return mCreationDate; +} + +void LLInventoryObject::setCreationDate(time_t creation_date_utc) +{ + mCreationDate = creation_date_utc; +} + +const std::string& LLInventoryItem::getDescription() const +{ + return mDescription; +} + +const std::string& LLInventoryItem::getActualDescription() const +{ + return mDescription; +} ///---------------------------------------------------------------------------- /// Class LLInventoryItem @@ -299,9 +314,9 @@ LLInventoryItem::LLInventoryItem(const LLUUID& uuid, mDescription(desc), mSaleInfo(sale_info), mInventoryType(inv_type), - mFlags(flags), - mCreationDate(creation_date_utc) + mFlags(flags) { + mCreationDate = creation_date_utc; LLStringUtil::replaceNonstandardASCII(mDescription, ' '); LLStringUtil::replaceChar(mDescription, '|', ' '); mPermissions.initMasks(inv_type); @@ -314,9 +329,9 @@ LLInventoryItem::LLInventoryItem() : mDescription(), mSaleInfo(), mInventoryType(LLInventoryType::IT_NONE), - mFlags(0), - mCreationDate(0) + mFlags(0) { + mCreationDate = (time_t)0; } LLInventoryItem::LLInventoryItem(const LLInventoryItem* other) : @@ -376,21 +391,6 @@ void LLInventoryItem::setAssetUUID(const LLUUID& asset_id) } -const std::string& LLInventoryItem::getDescription() const -{ - return mDescription; -} - -const std::string& LLInventoryItem::getActualDescription() const -{ - return mDescription; -} - -time_t LLInventoryItem::getCreationDate() const -{ - return mCreationDate; -} - U32 LLInventoryItem::getCRC32() const { // *FIX: Not a real crc - more of a checksum. @@ -447,11 +447,6 @@ void LLInventoryItem::setFlags(U32 flags) mFlags = flags; } -void LLInventoryItem::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; -} - // Currently only used in the Viewer to handle calling cards // where the creator is actually used to store the target. void LLInventoryItem::setCreator(const LLUUID& creator) @@ -513,6 +508,12 @@ U32 LLInventoryItem::getFlags() const return mFlags; } +time_t LLInventoryItem::getCreationDate() const +{ + return mCreationDate; +} + + // virtual void LLInventoryItem::packMessage(LLMessageSystem* msg) const { @@ -1047,11 +1048,17 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const LLFastTimer::DeclareTimer FTM_INVENTORY_SD_DESERIALIZE("Inventory SD Deserialize"); -bool LLInventoryItem::fromLLSD(const LLSD& sd) +bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) { + LLFastTimer _(FTM_INVENTORY_SD_DESERIALIZE); - mInventoryType = LLInventoryType::IT_NONE; - mAssetUUID.setNull(); + if (is_new) + { + // If we're adding LLSD to an existing object, need avoid + // clobbering these fields. + mInventoryType = LLInventoryType::IT_NONE; + mAssetUUID.setNull(); + } std::string w; w = INV_ITEM_ID_LABEL; @@ -1108,6 +1115,11 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd) { mAssetUUID = sd[w]; } + w = INV_LINKED_ID_LABEL; + if (sd.has(w)) + { + mAssetUUID = sd[w]; + } w = INV_ASSET_TYPE_LABEL; if (sd.has(w)) { diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index c93b0f07a..ce58bbb2d 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -73,6 +73,7 @@ public: virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items BOOL getIsLinkType() const; + virtual time_t getCreationDate() const; //-------------------------------------------------------------------- // Mutators @@ -83,6 +84,7 @@ public: virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); void setType(LLAssetType::EType type); + virtual void setCreationDate(time_t creation_date_utc); // only stored for items // [RLVa:KB] - Checked: 2014-01-07 (RLVa-1.4.10) // in place correction for inventory name string @@ -103,7 +105,6 @@ public: virtual BOOL importLegacyStream(std::istream& input_stream); virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; - virtual void removeFromServer(); virtual void updateParentOnServer(BOOL) const; virtual void updateServer(BOOL) const; @@ -115,6 +116,7 @@ protected: LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL. LLAssetType::EType mType; std::string mName; + time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -180,7 +182,6 @@ public: void setPermissions(const LLPermissions& perm); void setInventoryType(LLInventoryType::EType inv_type); void setFlags(U32 flags); - void setCreationDate(time_t creation_date_utc); void setCreator(const LLUUID& creator); // only used for calling cards // Check for changes in permissions masks and sale info @@ -214,7 +215,7 @@ public: void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size); LLSD asLLSD() const; void asLLSD( LLSD& sd ) const; - bool fromLLSD(const LLSD& sd); + bool fromLLSD(const LLSD& sd, bool is_new = true); //-------------------------------------------------------------------- // Member Variables @@ -226,7 +227,6 @@ protected: LLSaleInfo mSaleInfo; LLInventoryType::EType mInventoryType; U32 mFlags; - time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index b8b2f5a1e..b0deafdfe 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -535,7 +535,7 @@ void LLPermissions::packMessage(LLMessageSystem* msg) const msg->addBOOLFast(_PREHASH_GroupOwned, (BOOL)mIsGroupOwned); } -void LLPermissions::unpackMessage(LLSD perms) +void LLPermissions::unpackMessage(const LLSD& perms) { mCreator = perms["creator-id"]; mOwner = perms["owner-id"]; diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index e6284e542..d3a65a5a6 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -319,7 +319,7 @@ public: // LLSD packMessage() const; - void unpackMessage(LLSD perms); + void unpackMessage(const LLSD& perms); // For messaging system support void packMessage(LLMessageSystem* msg) const; diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 30acb7f3a..2c5d163e3 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -236,7 +236,7 @@ void LLSaleInfo::packMessage(LLMessageSystem* msg) const //msg->addU32Fast(_PREHASH_NextOwnerMask, mNextOwnerPermMask); } -void LLSaleInfo::unpackMessage(LLSD sales) +void LLSaleInfo::unpackMessage(const LLSD& sales) { U8 sale_type = (U8)sales["sale-type"].asInteger(); mSaleType = static_cast(sale_type); diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h index 026e042bc..8a01653a6 100644 --- a/indra/llinventory/llsaleinfo.h +++ b/indra/llinventory/llsaleinfo.h @@ -102,7 +102,7 @@ public: BOOL importStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask); LLSD packMessage() const; - void unpackMessage(LLSD sales); + void unpackMessage(const LLSD& sales); // message serialization void packMessage(LLMessageSystem* msg) const; diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index 9ecec8c72..0dc29f5be 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -80,7 +80,7 @@ typedef boost::intrusive_ptr AIPe enum AICapabilityType { // {Capabilities} [Responders] cap_texture = 0, // GetTexture [HTTPGetResponder] - cap_inventory = 1, // { FetchInventory2, FetchLib2 } [LLInventoryModel::fetchInventoryResponder], { FetchInventoryDescendents2, FetchLibDescendents2 } [LLInventoryModelFetchDescendentsResponder] + cap_inventory = 1, // { FetchInventory2, FetchLib2 } [LLInventoryModel::FetchItemHttpHandler], { FetchInventoryDescendents2, FetchLibDescendents2 } [GItemHttpHandler] cap_mesh = 2, // GetMesh [LLMeshSkinInfoResponder, LLMeshDecompositionResponder, LLMeshPhysicsShapeResponder, LLMeshHeaderResponder, LLMeshLODResponder] cap_other = 3, // All other capabilities diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 72678dec0..86bd75370 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -917,7 +917,7 @@ P(emeraldDicDownloader); P(environmentApplyResponder); P(environmentRequestResponder); P2(eventPollResponder, reply_60s); -P(fetchInventoryResponder); +P(FetchItemHttpHandler); P(fetchScriptLimitsAttachmentInfoResponder); P(fetchScriptLimitsRegionDetailsResponder); P(fetchScriptLimitsRegionInfoResponder); @@ -930,8 +930,8 @@ P(homeLocationResponder); P2(HTTPGetResponder, reply_15s); P(iamHere); P(iamHereVoice); -P2(inventoryModelFetchDescendentsResponder, transfer_300s); -P(inventoryModelFetchItemResponder); +P2(BGFolderHttpHandler, transfer_300s); +P(BGItemHttpHandler); P(lcl_responder); P(mapLayerResponder); P(materialsResponder); @@ -951,6 +951,7 @@ P(productInfoRequestResponder); P(regionResponder); P(remoteParcelRequestResponder); P(requestAgentUpdateAppearance); +P2(incrementCofVersionResponder_timeouts, transfer_30s_connect_10s); P(responderIgnore); P(setDisplayNameResponder); P2(simulatorFeaturesReceived, transfer_22s_connect_10s); diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 2878ca456..863a28469 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -110,7 +110,7 @@ class LLSDInjector : public Injector class RawInjector : public Injector { public: - RawInjector(char const* data, U32 size) : mData(data), mSize(size) { } + RawInjector(U8 const* data, U32 size) : mData(data), mSize(size) { } /*virtual*/ ~RawInjector() { delete [] mData; } /*virtual*/ char const* contentType(void) const { return "application/octet-stream"; } @@ -118,12 +118,12 @@ class RawInjector : public Injector /*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { LLBufferStream ostream(channels, buffer.get()); - ostream.write(mData, mSize); + ostream.write((const char*)mData, mSize); ostream << std::flush; // Always flush a LLBufferStream when done writing to it. return mSize; } - char const* mData; + U8 const* mData; U32 mSize; }; @@ -722,7 +722,7 @@ void LLHTTPClient::put(std::string const& url, LLSD const& body, ResponderPtr re request(url, HTTP_PUT, new LLSDInjector(body), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), no_keep_alive, no_does_authentication, no_allow_compressed_reply); } -void LLHTTPClient::putRaw(const std::string& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug)) +void LLHTTPClient::putRaw(const std::string& url, const U8* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug)) { request(url, HTTP_PUT, new RawInjector(data, size), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), no_keep_alive, no_does_authentication, no_allow_compressed_reply); } @@ -753,7 +753,7 @@ void LLHTTPClient::postXMLRPC(std::string const& url, char const* method, XMLRPC request(url, HTTP_POST, new XMLRPCInjector(xmlrpc_request), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive, does_authentication, no_allow_compressed_reply); } -void LLHTTPClient::postRaw(std::string const& url, char const* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive) +void LLHTTPClient::postRaw(std::string const& url, U8 const* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug), EKeepAlive keepalive) { request(url, HTTP_POST, new RawInjector(data, size), responder, headers, NULL/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive); } diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index bd24111f0..c0781f331 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -491,8 +491,8 @@ public: static void put(std::string const& url, LLSD const& body, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)) { AIHTTPHeaders headers; put(url, body, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug)); } - static void putRaw(const std::string& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)); - static void putRaw(const std::string& url, const char* data, S32 size, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)) + static void putRaw(const std::string& url, const U8* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)); + static void putRaw(const std::string& url, const U8* data, S32 size, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)) { AIHTTPHeaders headers; putRaw(url, data, size, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug)); } static void getHeaderOnly(std::string const& url, ResponderHeadersOnly* responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off)); @@ -517,8 +517,8 @@ public: { AIHTTPHeaders headers; postXMLRPC(url, method, value, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive); } /** Takes ownership of data and deletes it when sent */ - static void postRaw(std::string const& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive); - static void postRaw(std::string const& url, const char* data, S32 size, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive) + static void postRaw(std::string const& url, const U8* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive); + static void postRaw(std::string const& url, const U8* data, S32 size, ResponderPtr responder/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive) { AIHTTPHeaders headers; postRaw(url, data, size, responder, headers/*,*/ DEBUG_CURLIO_PARAM(debug), keepalive); } static void postFile(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers/*,*/ DEBUG_CURLIO_PARAM(EDebugCurl debug = debug_off), EKeepAlive keepalive = keep_alive); diff --git a/indra/llui/llmemberlistener.h b/indra/llui/llmemberlistener.h index 0760213c7..a70ab926b 100644 --- a/indra/llui/llmemberlistener.h +++ b/indra/llui/llmemberlistener.h @@ -82,5 +82,24 @@ protected: std::string mRegisteredName; }; +class LLBindMemberListener : public LLOldEvents::LLSimpleListener +{ +public: + typedef boost::function event, const LLSD& userdata)> event_callback_t; + template + LLBindMemberListener(T *pointer, const std::string& register_name, event_callback_t cb) + { + pointer->registerEventListener(register_name, this); + mCallback = cb; + } + + // This is what you have to override to handle this event + virtual bool handleEvent(LLPointer event, const LLSD& userdata) + { + mCallback(event, userdata); + return true; + } + event_callback_t mCallback; +}; #endif // LL_LLMEMBERLISTENER_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b39c82054..8799c86d4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -296,6 +296,7 @@ set(viewer_SOURCE_FILES llgroupnotify.cpp llhomelocationresponder.cpp llhoverview.cpp + llhttpretrypolicy.cpp llhudeffect.cpp llhudeffectbeam.cpp llhudeffectlookat.cpp @@ -824,6 +825,7 @@ set(viewer_HEADER_FILES llgroupactions.h llgroupmgr.h llgroupnotify.h + llhttpretrypolicy.h llhomelocationresponder.h llhoverview.h llhudeffect.h diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index d0bbd1830..7d4fa037f 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -7383,7 +7383,7 @@ outputRezTiming("Sending wearables request"); - sendAgentWearablesRequest(); + if(!gHippoGridManager->getConnectedGrid()->isSecondLife()) + { + avatar->outputRezTiming("Sending wearables request"); + sendAgentWearablesRequest(); + } setAvatarAppearance(avatar); } } @@ -260,7 +264,7 @@ LLAgentWearables::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCal * @param wearable The wearable data. * @param todo Bitmask of actions to take on completion. */ -LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback( +LLAgentWearables::AddWearableToAgentInventoryCallback::AddWearableToAgentInventoryCallback( LLPointer cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, U32 todo, const std::string description) : mType(type), mIndex(index), @@ -272,7 +276,7 @@ LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInvento LL_INFOS() << "constructor" << LL_ENDL; } -void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item) +void LLAgentWearables::AddWearableToAgentInventoryCallback::fire(const LLUUID& inv_item) { if (mTodo & CALL_CREATESTANDARDDONE) { @@ -307,7 +311,8 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i } if (mTodo & CALL_WEARITEM) { - LLAppearanceMgr::instance().addCOFItemLink(inv_item, true, NULL, mDescription); + LLAppearanceMgr::instance().addCOFItemLink(inv_item, + new LLUpdateAppearanceAndEditWearableOnDestroy(inv_item), mDescription); } } @@ -320,7 +325,7 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::ETy if (item_id.isNull()) return; - + LLUUID old_item_id = getWearableItemID(type,index); if (wearable) @@ -328,7 +333,7 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::ETy wearable->setItemID(item_id); if (old_item_id.notNull()) - { + { gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id); setWearable(type,index,wearable); } @@ -341,7 +346,7 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::ETy gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); LLViewerInventoryItem* item = gInventory.getItem(item_id); - if(item && wearable) + if (item && wearable) { // We're changing the asset id, so we both need to set it // locally via setAssetUUID() and via setTransactionID() which @@ -367,12 +372,12 @@ void LLAgentWearables::sendAgentWearablesUpdate() if (wearable->getItemID().isNull()) { LLPointer cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer(NULL), (LLWearableType::EType)type, index, wearable, - addWearableToAgentInventoryCallback::CALL_NONE); + AddWearableToAgentInventoryCallback::CALL_NONE); addWearableToAgentInventory(cb, wearable); } else @@ -470,23 +475,18 @@ void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32 item->getFlags(), item->getCreationDate()); template_item->setTransactionID(new_wearable->getTransactionID()); - template_item->updateServer(FALSE); - gInventory.updateItem(template_item); - if (name_changed) - { - gInventory.notifyObservers(); - } + update_inventory_item(template_item, gAgentAvatarp->mEndCustomizeCallback); } else { // Add a new inventory item (shouldn't ever happen here) - U32 todo = addWearableToAgentInventoryCallback::CALL_NONE; + U32 todo = AddWearableToAgentInventoryCallback::CALL_NONE; if (send_update) { - todo |= addWearableToAgentInventoryCallback::CALL_UPDATE; + todo |= AddWearableToAgentInventoryCallback::CALL_UPDATE; } LLPointer cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer(NULL), type, index, @@ -539,12 +539,12 @@ LLViewerWearable* LLAgentWearables::saveWearableAs(const LLWearableType::EType t trunc_description); LLPointer cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer(NULL), type, index, new_wearable, - addWearableToAgentInventoryCallback::CALL_WEARITEM, + AddWearableToAgentInventoryCallback::CALL_WEARITEM, trunc_description ); LLUUID category_id; @@ -826,10 +826,13 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed) // the versions themselves are compatible. This code can be removed before release. if( wearable->getDefinitionVersion() == 24 ) { - wearable->setDefinitionVersion(22); - U32 index = getWearableIndex(wearable); - saveWearable(wearable->getType(),index,TRUE); + U32 index; + if (getWearableIndex(wearable,index)) + { LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL; + wearable->setDefinitionVersion(22); + saveWearable(wearable->getType(),index,TRUE); + } } //Needed as wearable 'save' process is a mess and fires superfluous updateScrollingPanelList calls @@ -881,11 +884,13 @@ void LLAgentWearables::getWearableItemIDs(LLWearableType::EType eType, uuid_vec_ if (mWearableDatas.end() != itWearableType) { for (wearableentry_vec_t::const_iterator itWearable = itWearableType->second.begin(); - itWearable != itWearableType->second.end(); ++itWearable) + itWearable != itWearableType->second.end(); ++itWearable) { - LLViewerWearable* wearable = dynamic_cast(*itWearable); - if(wearable) - idItems.push_back(wearable->getItemID()); + const LLViewerWearable* pWearable = dynamic_cast(*itWearable); + if (pWearable) + { + idItems.push_back(pWearable->getItemID()); + } } } } @@ -1033,12 +1038,12 @@ void LLAgentWearables::recoverMissingWearable(const LLWearableType::EType type, // destory content.) JC const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); LLPointer cb = - new addWearableToAgentInventoryCallback( + new AddWearableToAgentInventoryCallback( LLPointer(NULL), type, index, new_wearable, - addWearableToAgentInventoryCallback::CALL_RECOVERDONE); + AddWearableToAgentInventoryCallback::CALL_RECOVERDONE); addWearableToAgentInventory( cb, new_wearable, lost_and_found_id, TRUE); } @@ -1081,7 +1086,7 @@ public: /* virtual */ void fire(const LLUUID& inv_item) { LL_INFOS() << "One item created " << inv_item.asString() << LL_ENDL; - LLViewerInventoryItem *item = gInventory.getItem(inv_item); + LLConstPointer item = gInventory.getItem(inv_item); mItemsToLink.push_back(item); updatePendingWearable(inv_item); } @@ -1089,9 +1094,9 @@ public: { LL_INFOS() << "All items created" << LL_ENDL; LLPointer link_waiter = new LLUpdateAppearanceOnDestroy; - LLAppearanceMgr::instance().linkAll(LLAppearanceMgr::instance().getCOF(), - mItemsToLink, - link_waiter); + link_inventory_array(LLAppearanceMgr::instance().getCOF(), + mItemsToLink, + link_waiter); } void addPendingWearable(LLViewerWearable *wearable) { @@ -1140,7 +1145,7 @@ public: } private: - LLInventoryModel::item_array_t mItemsToLink; + LLInventoryObject::const_object_list_t mItemsToLink; std::vector mWearablesAwaitingItems; }; @@ -1195,6 +1200,39 @@ void LLAgentWearables::createStandardWearables() } } +// We no longer need this message in the current viewer, but send +// it for now to maintain compatibility with release viewers. Can +// remove this function once the SH-3455 changesets are universally deployed. +void LLAgentWearables::sendDummyAgentWearablesUpdate() +{ + LL_DEBUGS("Avatar") << "sendAgentWearablesUpdate()" << LL_ENDL; + + // Send the AgentIsNowWearing + gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing); + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + + // Send 4 standardized nonsense item ids (same as returned by the modified sim, not that it especially matters). + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(1)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("db5a4e5f-9da3-44c8-992d-1181c5795498")); + + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(2)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("6969c7cc-f72f-4a76-a19b-c293cce8ce4f")); + + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(3)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("7999702b-b291-48f9-8903-c91dfb828408")); + + gMessageSystem->nextBlockFast(_PREHASH_WearableData); + gMessageSystem->addU8Fast(_PREHASH_WearableType, U8(4)); + gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID("566cb59e-ef60-41d7-bfa6-e0f293fbea40")); + + gAgent.sendReliableMessage(); +} void LLAgentWearables::createStandardWearablesDone(S32 type, U32 index) { LL_INFOS() << "type " << type << " index " << index << LL_ENDL; @@ -1335,7 +1373,7 @@ void LLAgentWearables::removeWearableFinal( LLWearableType::EType type, bool do_ //queryWearableCache(); // moved below if (old_wearable) { - popWearable(old_wearable); + eraseWearable(old_wearable); old_wearable->removeFromAvatar(TRUE); } } @@ -1352,7 +1390,7 @@ void LLAgentWearables::removeWearableFinal( LLWearableType::EType type, bool do_ if (old_wearable) { - popWearable(old_wearable); + eraseWearable(old_wearable); old_wearable->removeFromAvatar(TRUE); } } @@ -1366,30 +1404,92 @@ void LLAgentWearables::removeWearableFinal( LLWearableType::EType type, bool do_ // Assumes existing wearables are not dirty. void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& items, - const std::vector< LLViewerWearable* >& wearables, - BOOL remove) + const std::vector< LLViewerWearable* >& wearables) { LL_INFOS() << "setWearableOutfit() start" << LL_ENDL; - // TODO: Removed check for ensuring that teens don't remove undershirt and underwear. Handle later - if (remove) - { - // note: shirt is the first non-body part wearable item. Update if wearable order changes. - // This loop should remove all clothing, but not any body parts - for (S32 type = 0; type < (S32)LLWearableType::WT_COUNT; type++) - { - if (LLWearableType::getAssetType((LLWearableType::EType)type) == LLAssetType::AT_CLOTHING) - { - removeWearable((LLWearableType::EType)type, true, 0); - } - } - } - S32 count = wearables.size(); llassert(items.size() == count); - S32 i; - for (i = 0; i < count; i++) + // Check for whether outfit already matches the one requested + S32 matched = 0, mismatched = 0; + const S32 arr_size = LLWearableType::WT_COUNT; + S32 type_counts[arr_size]; + std::fill(type_counts,type_counts+arr_size,0); + for (S32 i = 0; i < count; i++) + { + LLViewerWearable* new_wearable = wearables[i]; + LLPointer new_item = items[i]; + + const LLWearableType::EType type = new_wearable->getType(); + if (type < 0 || type>=LLWearableType::WT_COUNT) + { + LL_WARNS() << "invalid type " << type << LL_ENDL; + mismatched++; + continue; + } + S32 index = type_counts[type]; + type_counts[type]++; + + LLViewerWearable *curr_wearable = dynamic_cast(getWearable(type,index)); + if (!new_wearable || !curr_wearable || + new_wearable->getAssetID() != curr_wearable->getAssetID()) + { + LL_DEBUGS("Avatar") << "mismatch, type " << type << " index " << index + << " names " << (curr_wearable ? curr_wearable->getName() : "NONE") << "," + << " names " << (new_wearable ? new_wearable->getName() : "NONE") << LL_ENDL; + mismatched++; + continue; + } + + // Don't care about this case - ordering of wearables with the same asset id has no effect. + // Causes the two-alphas error case in MAINT-4158. + // We should actually disallow wearing two wearables with the same asset id. +#if 0 + if (curr_wearable->getName() != new_item->getName() || + curr_wearable->getItemID() != new_item->getUUID()) + { + LL_DEBUGS("Avatar") << "mismatch on name or inventory id, names " + << curr_wearable->getName() << " vs " << new_item->getName() + << " item ids " << curr_wearable->getItemID() << " vs " << new_item->getUUID() + << LL_ENDL; + mismatched++; + continue; + } +#endif + // If we got here, everything matches. + matched++; + } + LL_DEBUGS("Avatar") << "matched " << matched << " mismatched " << mismatched << LL_ENDL; + for (S32 j=0; j new_item = items[i]; @@ -1422,6 +1522,7 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it if (isAgentAvatarValid()) { gAgentAvatarp->setCompositeUpdatesEnabled(TRUE); + gAgentAvatarp->writeWearablesToAvatar(); gAgentAvatarp->updateVisualParams(); // If we have not yet declouded, we may want to use @@ -1442,7 +1543,6 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it if (!mInitialWearablesLoaded) { mInitialWearablesLoaded = true; - mInitialWearablesLoadedSignal(); } // [/SL:KB] notifyLoadingFinished(); @@ -1511,7 +1611,13 @@ bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD& { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLInventoryItem* new_item = gInventory.getItem( notification["payload"]["item_id"].asUUID()); - U32 index = gAgentWearables.getWearableIndex(wearable); + U32 index; + if (!gAgentWearables.getWearableIndex(wearable,index)) + { + LL_WARNS() << "Wearable not found" << LL_ENDL; + delete wearable; + return false; + } if( !new_item ) { delete wearable; @@ -1550,11 +1656,11 @@ void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLViewerWeara if (do_append && getWearableItemID(type,0).notNull()) { new_wearable->setItemID(new_item->getUUID()); - /*mWearableDatas[type].push_back(new_wearable); - checkWearableAgainstInventory(new_wearable);*/ - pushWearable(type,new_wearable); //To call LLAgentWearables::wearableUpdated + const bool trigger_updated = false; + pushWearable(type, new_wearable, trigger_updated); LL_INFOS() << "Added additional wearable for type " << type << " size is now " << getWearableCount(type) << LL_ENDL; + checkWearableAgainstInventory(new_wearable); } else { @@ -1653,33 +1759,12 @@ void LLAgentWearables::invalidateBakedTextureHash(LLMD5& hash) const } } -// User has picked "remove from avatar" from a menu. -// static -//void LLAgentWearables::userRemoveWearable(const LLWearableType::EType &type, const U32 &index) -//{ -// if (!(type==LLWearableType::WT_SHAPE || type==LLWearableType::WT_SKIN || type==LLWearableType::WT_HAIR || type==LLWearableType::WT_EYES)) //&& -// //!((!gAgent.isTeen()) && (type==LLWearableType::WT_UNDERPANTS || type==LLWearableType::WT_UNDERSHIRT))) -// { -// gAgentWearables.removeWearable(type,false,index); -// } -//} - -//static -//void LLAgentWearables::userRemoveWearablesOfType(const LLWearableType::EType &type) -//{ -// if (!(type==LLWearableType::WT_SHAPE || type==LLWearableType::WT_SKIN || type==LLWearableType::WT_HAIR || type==LLWearableType::WT_EYES)) //&& -// //!((!gAgent.isTeen()) && (type==LLWearableType::WT_UNDERPANTS || type==LLWearableType::WT_UNDERSHIRT))) -// { -// gAgentWearables.removeWearable(type,true,0); -// } -//} - -// Combines userRemoveAllAttachments() and userAttachMultipleAttachments() logic to -// get attachments into desired state with minimal number of adds/removes. -//void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array) -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2) -void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array, bool attach_only) -// [/SL:KB] +/// Given a desired set of attachments, find what objects need to be +// removed, and what additional inventory items need to be added. +void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array_t& obj_item_array, + llvo_vec_t& objects_to_remove, + llvo_vec_t& objects_to_retain, + LLInventoryModel::item_array_t& items_to_add) { // Possible cases: // already wearing but not in request set -> take off. @@ -1691,10 +1776,12 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj std::set requested_item_ids; std::set current_item_ids; for (S32 i=0; igetLinkedUUID()); - + { + const LLUUID & requested_id = obj_item_array[i].get()->getLinkedUUID(); + //LL_INFOS() << "Requested attachment id " << requested_id << LL_ENDL; + requested_item_ids.insert(requested_id); + } // Build up list of objects to be removed and items currently attached. - llvo_vec_t objects_to_remove; for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); iter != gAgentAvatarp->mAttachmentPoints.end();) { @@ -1708,22 +1795,33 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj if (objectp) { LLUUID object_item_id = objectp->getAttachmentItemID(); + + bool remove_attachment = true; if (requested_item_ids.find(object_item_id) != requested_item_ids.end()) - { - // Object currently worn, was requested. + { // Object currently worn, was requested to keep it // Flag as currently worn so we won't have to add it again. - current_item_ids.insert(object_item_id); + remove_attachment = false; + } + else if (objectp->isTempAttachment()) + { // Check if we should keep this temp attachment + remove_attachment = LLAppearanceMgr::instance().shouldRemoveTempAttachment(objectp->getID()); + } + + if (remove_attachment) + { + // LL_INFOS() << "found object to remove, id " << objectp->getID() << ", item " << objectp->getAttachmentItemID() << LL_ENDL; + objects_to_remove.push_back(objectp); } else { - // object currently worn, not requested. - objects_to_remove.push_back(objectp); + // LL_INFOS() << "found object to keep, id " << objectp->getID() << ", item " << objectp->getAttachmentItemID() << LL_ENDL; + current_item_ids.insert(object_item_id); + objects_to_retain.push_back(objectp); } } } } - LLInventoryModel::item_array_t items_to_add; for (LLInventoryModel::item_array_t::iterator it = obj_item_array.begin(); it != obj_item_array.end(); ++it) @@ -1742,18 +1840,6 @@ void LLAgentWearables::userUpdateAttachments(LLInventoryModel::item_array_t& obj // S32 remove_count = objects_to_remove.size(); // S32 add_count = items_to_add.size(); // LL_INFOS() << "remove " << remove_count << " add " << add_count << LL_ENDL; - - // Remove everything in objects_to_remove -// userRemoveMultipleAttachments(objects_to_remove); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2) - if (!attach_only) - { - userRemoveMultipleAttachments(objects_to_remove); - } -// [/SL:KB] - - // Add everything in items_to_add - userAttachMultipleAttachments(items_to_add); } void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remove) @@ -1821,7 +1907,7 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra const LLInventoryItem* pItem = obj_item_array.at(idxItem).get(); if (!gRlvAttachmentLocks.canAttach(pItem)) { - obj_item_array.erase(obj_item_array.begin() + idxItem); + obj_item_array.erase( obj_item_array.begin() + idxItem ); RLV_ASSERT(false); } } @@ -1938,7 +2024,7 @@ bool LLAgentWearables::canWearableBeRemoved(const LLViewerWearable* wearable) co return !(((type == LLWearableType::WT_SHAPE) || (type == LLWearableType::WT_SKIN) || (type == LLWearableType::WT_HAIR) || (type == LLWearableType::WT_EYES)) && (getWearableCount(type) <= 1) ); } -void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake) +void LLAgentWearables::animateAllWearableParams(F32 delta, bool upload_bake) { for( S32 type = 0; type < LLWearableType::WT_COUNT; ++type ) { @@ -2012,7 +2098,16 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp); LLAssetType::EType asset_type = wearable->getAssetType(); LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; - LLPointer cb = new LLBoostFuncInventoryCallback(wear ? wear_and_edit_cb : wear_cb); + LLPointer cb; + if(wear) + { + cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb); + } + else + { + cb = new LLBoostFuncInventoryCallback(wear_cb); + } + LLUUID folder_id; if (parent_id.notNull()) @@ -2031,8 +2126,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con wearable->getTransactionID(), wearable->getName(), wearable->getDescription(), - asset_type, - inv_type, + asset_type, inv_type, wearable->getType(), LLFloaterPerms::getNextOwnerPerms("Wearables"), cb); @@ -2118,13 +2212,6 @@ boost::signals2::connection LLAgentWearables::addLoadedCallback(loaded_callback_ return mLoadedSignal.connect(cb); } -// [SL:KB] - Patch: Appearance-InitialWearablesLoadedCallback | Checked: 2010-08-14 (Catznip-2.1) -boost::signals2::connection LLAgentWearables::addInitialWearablesLoadedCallback(loaded_callback_t cb) -{ - return mInitialWearablesLoadedSignal.connect(cb); -} -// [/SL:KB] - bool LLAgentWearables::changeInProgress() const { return mCOFChangeInProgress; @@ -2133,6 +2220,7 @@ bool LLAgentWearables::changeInProgress() const void LLAgentWearables::notifyLoadingStarted() { mCOFChangeInProgress = true; + mCOFChangeTimer.reset(); mLoadingStartedSignal(); } diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 29ba8f076..4475da01e 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -80,6 +80,7 @@ public: bool areInitalWearablesLoaded() const { return mInitialWearablesLoaded; } // [/SL:KB] bool isCOFChangeInProgress() const { return mCOFChangeInProgress; } + F32 getCOFChangeTime() const { return mCOFChangeTimer.getElapsedTimeF32(); } void updateWearablesLoaded(); void checkWearablesLoaded() const; bool canMoveWearable(const LLUUID& item_id, bool closer_to_body) const; @@ -87,7 +88,7 @@ public: // Note: False for shape, skin, eyes, and hair, unless you have MORE than 1. bool canWearableBeRemoved(const LLViewerWearable* wearable) const; - void animateAllWearableParams(F32 delta, BOOL upload_bake); + void animateAllWearableParams(F32 delta, bool upload_bake = false); //-------------------------------------------------------------------- // Accessors @@ -114,7 +115,7 @@ private: /*virtual*/void wearableUpdated(LLWearable *wearable, BOOL removed); public: // void setWearableItem(LLInventoryItem* new_item, LLViewerWearable* wearable, bool do_append = false); - void setWearableOutfit(const LLInventoryItem::item_array_t& items, const std::vector< LLViewerWearable* >& wearables, BOOL remove); + void setWearableOutfit(const LLInventoryItem::item_array_t& items, const std::vector< LLViewerWearable* >& wearables); void setWearableName(const LLUUID& item_id, const std::string& new_name); void nameOrDescriptionChanged(LLUUID const& item_id); // *TODO: Move this into llappearance/LLWearableData ? @@ -198,6 +199,11 @@ public: void saveAllWearables(); void revertWearable(const LLWearableType::EType type, const U32 index); + // We no longer need this message in the current viewer, but send + // it for now to maintain compatibility with release viewers. Can + // remove this function once the SH-3455 changesets are universally deployed. + void sendDummyAgentWearablesUpdate(); + //-------------------------------------------------------------------- // Static UI hooks //-------------------------------------------------------------------- @@ -207,11 +213,10 @@ public: typedef std::vector llvo_vec_t; -// static void userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-3.0.0a) | Added: Catznip-2.2.0a - // Not the best way to go about this but other attempts changed far too much LL code to be a viable solution - static void userUpdateAttachments(LLInventoryModel::item_array_t& obj_item_array, bool fAttachOnly = false); -// [/SL:KB] + static void findAttachmentsAddRemoveInfo(LLInventoryModel::item_array_t& obj_item_array, + llvo_vec_t& objects_to_remove, + llvo_vec_t& objects_to_retain, + LLInventoryModel::item_array_t& items_to_add); static void userRemoveMultipleAttachments(llvo_vec_t& llvo_array); static void userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array); @@ -229,9 +234,6 @@ public: typedef boost::function loaded_callback_t; typedef boost::signals2::signal loaded_signal_t; boost::signals2::connection addLoadedCallback(loaded_callback_t cb); -// [SL:KB] - Patch: Appearance-InitialWearablesLoadedCallback | Checked: 2010-08-14 (Catznip-3.0.0a) | Added: Catznip-2.1.1d - boost::signals2::connection addInitialWearablesLoadedCallback(loaded_callback_t cb); -// [/SL:KB] bool changeInProgress() const; void notifyLoadingStarted(); @@ -240,9 +242,6 @@ public: private: loading_started_signal_t mLoadingStartedSignal; // should be called before wearables are changed loaded_signal_t mLoadedSignal; // emitted when all agent wearables get loaded -// [SL:KB] - Patch: Appearance-InitialWearablesLoadedCallback | Checked: 2010-08-14 (Catznip-3.0.0a) | Added: Catznip-2.1.1d - loaded_signal_t mInitialWearablesLoadedSignal; // emitted once when the initial wearables are loaded -// [/SL:KB] //-------------------------------------------------------------------- // Member variables @@ -259,6 +258,7 @@ private: * True if agent's outfit is being changed now. */ BOOL mCOFChangeInProgress; + LLTimer mCOFChangeTimer; //-------------------------------------------------------------------------------- // Support classes @@ -275,7 +275,7 @@ private: ~sendAgentWearablesUpdateCallback(); }; - class addWearableToAgentInventoryCallback : public LLInventoryCallback + class AddWearableToAgentInventoryCallback : public LLInventoryCallback { public: enum ETodo @@ -288,7 +288,7 @@ private: CALL_WEARITEM = 16 }; - addWearableToAgentInventoryCallback(LLPointer cb, + AddWearableToAgentInventoryCallback(LLPointer cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index 9fbf401c9..9b8d91af9 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -239,7 +239,7 @@ public: void doneIdle() { // NOTE: the code above makes the assumption that COF is empty which won't be the case the way it's used now - LLInventoryModel::item_array_t initial_items; + LLInventoryObject::const_object_list_t initial_items; for (uuid_vec_t::iterator itItem = mIDs.begin(); itItem != mIDs.end(); ++itItem) { LLViewerInventoryItem* pItem = gInventory.getItem(*itItem); @@ -415,7 +415,7 @@ void LLLibraryOutfitsFetch::folderDone() } mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); - mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); + mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING, false); // If Library->Clothing->Initial Outfits exists, use that. LLNameCategoryCollector matchFolderFunctor("Initial Outfits"); @@ -666,13 +666,9 @@ void LLLibraryOutfitsFetch::contentsDone() wearable_iter != wearable_array.end(); ++wearable_iter) { - const LLViewerInventoryItem *item = wearable_iter->get(); - link_inventory_item(gAgent.getID(), - item->getLinkedUUID(), - new_outfit_folder_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK, + LLConstPointer item = wearable_iter->get(); + link_inventory_object(new_outfit_folder_id, + item, order_myoutfits_on_destroy); } } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 5d44aa1a8..96b6990c2 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -26,6 +26,7 @@ #include "llviewerprecompiledheaders.h" +#include #include "llagent.h" #include "llagentcamera.h" #include "llagentwearables.h" @@ -49,6 +50,7 @@ #include "llviewerstats.h" #include "llwearablelist.h" #include "llsdutil.h" +#include "llhttpretrypolicy.h" #include "llinventorypanel.h" #include "llfloatercustomize.h" // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) @@ -433,106 +435,67 @@ private: S32 LLCallAfterInventoryCopyMgr::sInstanceCount = 0; -class LLCallAfterInventoryLinkMgr: public LLCallAfterInventoryBatchMgr +class LLWearCategoryAfterCopy: public LLInventoryCallback { public: - LLCallAfterInventoryLinkMgr(LLInventoryModel::item_array_t& src_items, - const LLUUID& dst_cat_id, - const std::string& phase_name, - nullary_func_t on_completion_func, - nullary_func_t on_failure_func = no_op, - F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL, - S32 max_retries = DEFAULT_MAX_RETRIES - ): - LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries) - { - addItems(src_items); - } - - virtual bool requestOperation(const LLUUID& item_id) - { - bool request_sent = false; - LLViewerInventoryItem *item = gInventory.getItem(item_id); - if (item) - { - if (item->getParentUUID() == mDstCatID) - { - LL_DEBUGS("Avatar") << "item " << item_id << " name " << item->getName() << " is already a child of " << mDstCatID << LL_ENDL; - return false; - } - LL_DEBUGS("Avatar") << "linking item " << item_id << " name " << item->getName() << " to " << mDstCatID << LL_ENDL; - // create an inventory item link. - if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate")) - { - LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << LL_ENDL; - return true; - } - link_inventory_item(gAgent.getID(), - item->getLinkedUUID(), - mDstCatID, - item->getName(), - item->getActualDescription(), - LLAssetType::AT_LINK, - new LLBoostFuncInventoryCallback( - boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer()))); - return true; - } - else - { - // create a base outfit link if appropriate. - LLViewerInventoryCategory *catp = gInventory.getCategory(item_id); - if (!catp) - { - LL_WARNS() << "link request failed, id not found as inventory item or category " << item_id << LL_ENDL; - return false; - } - const LLUUID cof = LLAppearanceMgr::instance().getCOF(); - std::string new_outfit_name = ""; + LLWearCategoryAfterCopy(bool append): + mAppend(append) + {} - LLAppearanceMgr::instance().purgeBaseOutfitLink(cof); - - if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) - { - if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate")) - { - LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << LL_ENDL; - return true; - } - LL_DEBUGS("Avatar") << "linking folder " << item_id << " name " << catp->getName() << " to cof " << cof << LL_ENDL; - link_inventory_item(gAgent.getID(), item_id, cof, catp->getName(), "", - LLAssetType::AT_LINK_FOLDER, - new LLBoostFuncInventoryCallback( - boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer()))); - new_outfit_name = catp->getName(); - request_sent = true; - } - - LLAppearanceMgr::instance().updatePanelOutfitName(new_outfit_name); - } - return request_sent; + // virtual + void fire(const LLUUID& id) + { + // Wear the inventory category. + LLInventoryCategory* cat = gInventory.getCategory(id); + LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, mAppend); } + +private: + bool mAppend; }; -LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering): +class LLTrackPhaseWrapper : public LLInventoryCallback +{ +public: + LLTrackPhaseWrapper(const std::string& phase_name, LLPointer cb = NULL): + mTrackingPhase(phase_name), + mCB(cb) + { + selfStartPhase(mTrackingPhase); + } + + // virtual + void fire(const LLUUID& id) + { + if (mCB) + { + mCB->fire(id); + } + } + + // virtual + ~LLTrackPhaseWrapper() + { + selfStopPhase(mTrackingPhase); + } + +protected: + std::string mTrackingPhase; + LLPointer mCB; +}; + +LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool enforce_item_restrictions, + bool enforce_ordering, + nullary_func_t post_update_func + ): mFireCount(0), - mUpdateBaseOrder(update_base_outfit_ordering) + mEnforceItemRestrictions(enforce_item_restrictions), + mEnforceOrdering(enforce_ordering), + mPostUpdateFunc(post_update_func) { selfStartPhase("update_appearance_on_destroy"); } -LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() -{ - if (!LLApp::isExiting()) - { - // speculative fix for MAINT-1150 - LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL; - - selfStopPhase("update_appearance_on_destroy"); - - LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder); - } -} - void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item) { LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item); @@ -543,6 +506,51 @@ void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item) mFireCount++; } +LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() +{ + if (!LLApp::isExiting()) + { + // speculative fix for MAINT-1150 + LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL; + + selfStopPhase("update_appearance_on_destroy"); + + LLAppearanceMgr::instance().updateAppearanceFromCOF(mEnforceItemRestrictions, + mEnforceOrdering, + mPostUpdateFunc); + } +} + +LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnDestroy(const LLUUID& item_id): + mItemID(item_id) +{ +} + +void edit_wearable_and_customize_avatar(LLUUID item_id) +{ + // Start editing the item if previously requested. + gAgentWearables.editWearableIfRequested(item_id); + + // TODO: camera mode may not be changed if a debug setting is tweaked + if( LLFloaterCustomize::instanceExists() ) + { + // If we're in appearance editing mode, the current tab may need to be refreshed + LLFloaterCustomize::getInstance()->switchToDefaultSubpart(); + } + +} + +LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOnDestroy() +{ + if (!LLApp::isExiting()) + { + LLAppearanceMgr::instance().updateAppearanceFromCOF( + true,true, + boost::bind(edit_wearable_and_customize_avatar, mItemID)); + } +} + + struct LLFoundData { LLFoundData() : @@ -605,15 +613,17 @@ public: found_list_t& getFoundList(); void eraseTypeToLink(LLWearableType::EType type); void eraseTypeToRecover(LLWearableType::EType type); -// void setObjItems(const LLInventoryModel::item_array_t& items); + void setObjItems(const LLInventoryModel::item_array_t& items); void setGestItems(const LLInventoryModel::item_array_t& items); bool isMostRecent(); void handleLateArrivals(); void resetTime(F32 timeout); + static S32 countActive() { return sActiveHoldingPatterns.size(); } + S32 index() { return mIndex; } private: found_list_t mFoundList; -// LLInventoryModel::item_array_t mObjItems; + LLInventoryModel::item_array_t mObjItems; LLInventoryModel::item_array_t mGestItems; typedef std::set type_set_t; type_set_t mTypesToRecover; @@ -623,12 +633,15 @@ private: bool mFired; typedef std::set type_set_hp; static type_set_hp sActiveHoldingPatterns; + static S32 sNextIndex; + S32 mIndex; bool mIsMostRecent; std::set mLateArrivals; bool mIsAllComplete; }; LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns; +S32 LLWearableHoldingPattern::sNextIndex = 0; LLWearableHoldingPattern::LLWearableHoldingPattern(): mResolved(0), @@ -636,13 +649,13 @@ LLWearableHoldingPattern::LLWearableHoldingPattern(): mIsMostRecent(true), mIsAllComplete(false) { - if (sActiveHoldingPatterns.size()>0) + if (countActive()>0) { LL_INFOS() << "Creating LLWearableHoldingPattern when " - << sActiveHoldingPatterns.size() - << " other attempts are active." - << " Flagging others as invalid." - << LL_ENDL; + << countActive() + << " other attempts are active." + << " Flagging others as invalid." + << LL_ENDL; for (type_set_hp::iterator it = sActiveHoldingPatterns.begin(); it != sActiveHoldingPatterns.end(); ++it) @@ -651,8 +664,10 @@ LLWearableHoldingPattern::LLWearableHoldingPattern(): } } + mIndex = sNextIndex++; sActiveHoldingPatterns.insert(this); - //selfStartPhase("holding_pattern"); + LL_DEBUGS("Avatar") << "HP " << index() << " created" << LL_ENDL; + selfStartPhase("holding_pattern"); } LLWearableHoldingPattern::~LLWearableHoldingPattern() @@ -662,6 +677,7 @@ LLWearableHoldingPattern::~LLWearableHoldingPattern() { selfStopPhase("holding_pattern"); } + LL_DEBUGS("Avatar") << "HP " << index() << " deleted" << LL_ENDL; } bool LLWearableHoldingPattern::isMostRecent() @@ -684,11 +700,10 @@ void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type) mTypesToRecover.erase(type); } -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-06-19 (Catznip-2.1) -//void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items) -//{ -// mObjItems = items; -//} +void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items) +{ + mObjItems = items; +} void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items) { @@ -751,7 +766,10 @@ void LLWearableHoldingPattern::checkMissingWearables() resetTime(60.0F); - selfStartPhase("get_missing_wearables"); + if (isMostRecent()) + { + selfStartPhase("get_missing_wearables_2"); + } if (!pollMissingWearables()) { doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this)); @@ -790,18 +808,49 @@ void LLWearableHoldingPattern::onAllComplete() } } - // Update wearables. - LL_INFOS("Avatar") << self_av_string() << "Updating agent wearables with " << mResolved << " wearable items " << LL_ENDL; - LLAppearanceMgr::instance().updateAgentWearables(this, false); - -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-03-22 (Catznip-2.1) -// // Update attachments to match those requested. -// if (isAgentAvatarValid()) -// { -// LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL; -// LL_INFOS() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL; -// LLAgentWearables::userUpdateAttachments(mObjItems); -// } + if (isAgentAvatarValid()) + { + LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL; + LLAgentWearables::llvo_vec_t objects_to_remove; + LLAgentWearables::llvo_vec_t objects_to_retain; + LLInventoryModel::item_array_t items_to_add; + + LLAgentWearables::findAttachmentsAddRemoveInfo(mObjItems, + objects_to_remove, + objects_to_retain, + items_to_add); + + LL_DEBUGS("Avatar") << self_av_string() << "Removing " << objects_to_remove.size() + << " attachments" << LL_ENDL; + + // Here we remove the attachment pos overrides for *all* + // attachments, even those that are not being removed. This is + // needed to get joint positions all slammed down to their + // pre-attachment states. + //gAgentAvatarp->clearAttachmentPosOverrides(); + + // Take off the attachments that will no longer be in the outfit. + LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove); + + // Update wearables. + LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " updating agent wearables with " + << mResolved << " wearable items " << LL_ENDL; + LLAppearanceMgr::instance().updateAgentWearables(this); + + // Restore attachment pos overrides for the attachments that + // are remaining in the outfit. + for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin(); + it != objects_to_retain.end(); + ++it) + { + //LLViewerObject *objectp = *it; + //gAgentAvatarp->addAttachmentPosOverridesForObject(objectp); + } + + // Add new attachments to match those requested. + LL_DEBUGS("Avatar") << self_av_string() << "Adding " << items_to_add.size() << " attachments" << LL_ENDL; + LLAgentWearables::userAttachMultipleAttachments(items_to_add); + } if (isFetchCompleted() && isMissingCompleted()) { @@ -817,7 +866,10 @@ void LLWearableHoldingPattern::onAllComplete() void LLWearableHoldingPattern::onFetchCompletion() { - selfStopPhase("get_wearables"); + if (isMostRecent()) + { + selfStopPhase("get_wearables_2"); + } if (!isMostRecent()) { @@ -849,7 +901,7 @@ bool LLWearableHoldingPattern::pollFetchCompletion() if (done) { - LL_INFOS("Avatar") << self_av_string() << "polling, done status: " << completed << " timed out " << timed_out + LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " polling, done status: " << completed << " timed out " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() << LL_ENDL; mFired = true; @@ -868,11 +920,11 @@ void recovered_item_link_cb(const LLUUID& item_id, LLWearableType::EType type, L { if (!holder->isMostRecent()) { - LL_WARNS() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL; + LL_WARNS() << "HP " << holder->index() << " skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL; // runway skip here? } - LL_INFOS() << "Recovered item link for type " << type << LL_ENDL; + LL_INFOS() << "HP " << holder->index() << " recovered item link for type " << type << LL_ENDL; holder->eraseTypeToLink(type); // Add wearable to FoundData for actual wearing LLViewerInventoryItem *item = gInventory.getItem(item_id); @@ -896,12 +948,12 @@ void recovered_item_link_cb(const LLUUID& item_id, LLWearableType::EType type, L } else { - LL_WARNS() << self_av_string() << "inventory item not found for recovered wearable" << LL_ENDL; + LL_WARNS() << self_av_string() << "inventory link not found for recovered wearable" << LL_ENDL; } } else { - LL_WARNS() << self_av_string() << "inventory link not found for recovered wearable" << LL_ENDL; + LL_WARNS() << self_av_string() << "HP " << holder->index() << " inventory link not found for recovered wearable" << LL_ENDL; } } @@ -919,20 +971,15 @@ void recovered_item_cb(const LLUUID& item_id, LLWearableType::EType type, LLView } LL_DEBUGS("Avatar") << self_av_string() << "Recovered item for type " << type << LL_ENDL; - LLViewerInventoryItem *itemp = gInventory.getItem(item_id); + LLConstPointer itemp = gInventory.getItem(item_id); wearable->setItemID(item_id); - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(recovered_item_link_cb,_1,type,wearable,holder)); holder->eraseTypeToRecover(type); llassert(itemp); if (itemp) { - link_inventory_item( gAgent.getID(), - item_id, - LLAppearanceMgr::instance().getCOF(), - itemp->getName(), - itemp->getDescription(), - LLAssetType::AT_LINK, - cb); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(recovered_item_link_cb,_1,type,wearable,holder)); + + link_inventory_object(LLAppearanceMgr::instance().getCOF(), itemp, cb); } } @@ -947,7 +994,7 @@ void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type // Try to recover by replacing missing wearable with a new one. LLNotificationsUtil::add("ReplacedMissingWearable"); LL_DEBUGS() << "Wearable " << LLWearableType::getTypeLabel(type) - << " could not be downloaded. Replaced inventory item with default wearable." << LL_ENDL; + << " could not be downloaded. Replaced inventory item with default wearable." << LL_ENDL; LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp); // Add a new one in the lost and found folder. @@ -980,7 +1027,7 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable)) { // Wearable link that was never resolved; remove links to it from COF - LL_INFOS("Avatar") << self_av_string() << "removing link for unresolved item " << data.mItemID.asString() << LL_ENDL; + LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " removing link for unresolved item " << data.mItemID.asString() << LL_ENDL; LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID); } } @@ -1019,7 +1066,7 @@ bool LLWearableHoldingPattern::pollMissingWearables() if (!done) { - LL_INFOS("Avatar") << self_av_string() << "polling missing wearables, waiting for items " << mTypesToRecover.size() + LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " polling missing wearables, waiting for items " << mTypesToRecover.size() << " links " << mTypesToLink.size() << " wearables, timed out " << timed_out << " elapsed " << mWaitTime.getElapsedTimeF32() @@ -1028,7 +1075,10 @@ bool LLWearableHoldingPattern::pollMissingWearables() if (done) { - selfStopPhase("get_missing_wearables"); + if (isMostRecent()) + { + selfStopPhase("get_missing_wearables_2"); + } gAgentAvatarp->debugWearablesLoaded(); @@ -1066,7 +1116,7 @@ void LLWearableHoldingPattern::handleLateArrivals() LL_WARNS() << self_av_string() << "Late arrivals not handled - in middle of missing wearables processing" << LL_ENDL; } - LL_INFOS("Avatar") << self_av_string() << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << LL_ENDL; + LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " need to handle " << mLateArrivals.size() << " late arriving wearables" << LL_ENDL; // Update mFoundList using late-arriving wearables. std::set replaced_types; @@ -1129,7 +1179,7 @@ void LLWearableHoldingPattern::handleLateArrivals() mLateArrivals.clear(); // Update appearance based on mFoundList - LLAppearanceMgr::instance().updateAgentWearables(this, false); + LLAppearanceMgr::instance().updateAgentWearables(this); } void LLWearableHoldingPattern::resetTime(F32 timeout) @@ -1146,7 +1196,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLViewerWearable *wearable) } mResolved += 1; // just counting callbacks, not successes. - LL_DEBUGS("Avatar") << self_av_string() << "resolved " << mResolved << "/" << getFoundList().size() << LL_ENDL; + LL_DEBUGS("Avatar") << self_av_string() << "HP " << index() << " resolved " << mResolved << "/" << getFoundList().size() << LL_ENDL; if (!wearable) { LL_WARNS() << self_av_string() << "no wearable found" << LL_ENDL; @@ -1212,6 +1262,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLViewerWearable *wearable) llassert(item->getUUID() == data.mItemID); item->setWearableType(wearable->getType()); gInventory.updateItem(item); + gInventory.notifyObservers(); } data.mWearable = wearable; @@ -1288,21 +1339,6 @@ S32 LLAppearanceMgr::getCOFVersion() const } } -S32 LLAppearanceMgr::getLastUpdateRequestCOFVersion() const -{ - return mLastUpdateRequestCOFVersion; -} - -S32 LLAppearanceMgr::getLastAppearanceUpdateCOFVersion() const -{ - return mLastAppearanceUpdateCOFVersion; -} - -void LLAppearanceMgr::setLastAppearanceUpdateCOFVersion(S32 new_val) -{ - mLastAppearanceUpdateCOFVersion = new_val; -} - const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() { const LLUUID& current_outfit_cat = getCOF(); @@ -1315,8 +1351,7 @@ const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() cat_array, item_array, false, - is_category, - false); + is_category); for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); iter != item_array.end(); iter++) @@ -1382,81 +1417,121 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace/*= false*/) } } -bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer cb) +void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, + bool do_update, + bool replace, + LLPointer cb) { - if (item_id_to_wear.isNull()) return false; + bool first = true; - // *TODO: issue with multi-wearable should be fixed: - // in this case this method will be called N times - loading started for each item - // and than N times will be called - loading completed for each item. - // That means subscribers will be notified that loading is done after first item in a batch is worn. - // (loading indicator disappears for example before all selected items are worn) - // Have not fix this issue for 2.1 because of stability reason. EXT-7777. + LLInventoryObject::const_object_list_t items_to_link; - // Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times -// gAgentWearables.notifyLoadingStarted(); + for (uuid_vec_t::const_iterator it = item_ids_to_wear.begin(); + it != item_ids_to_wear.end(); + ++it) + { + replace = first && replace; + first = false; - LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); - if (!item_to_wear) return false; + const LLUUID& item_id_to_wear = *it; - if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) - { - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace)); - copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb); - return false; - } - else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) - { - return false; // not in library and not in agent's inventory - } - else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) - { - LLNotificationsUtil::add("CannotWearTrash"); - return false; - } - else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911 - { - return false; - } + if (item_id_to_wear.isNull()) continue; + + LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); + if (!item_to_wear) continue; + + if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) + { + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace)); + copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb); + continue; + } + else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) + { + continue; // not in library and not in agent's inventory + } + else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) + { + LLNotificationsUtil::add("CannotWearTrash"); + continue; + } + else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911 + { + continue; + } // [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) replace |= (LLAssetType::AT_BODYPART == item_to_wear->getType()); // Body parts should always replace if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanWearItem(item_to_wear, (replace) ? RLV_WEAR_REPLACE : RLV_WEAR_ADD)) ) { - return false; + continue; } // [/RLVa:KB] - switch (item_to_wear->getType()) - { - case LLAssetType::AT_CLOTHING: - if (gAgentWearables.areWearablesLoaded()) - { - S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); - if ((replace && wearable_count != 0) || - (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) - { - removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1)); - } - addCOFItemLink(item_to_wear, do_update, cb); - } - break; - case LLAssetType::AT_BODYPART: - // TODO: investigate wearables may not be loaded at this point EXT-8231 - - // Remove the existing wearables of the same type. - // Remove existing body parts anyway because we must not be able to wear e.g. two skins. - removeCOFLinksOfType(item_to_wear->getWearableType()); + switch (item_to_wear->getType()) + { + case LLAssetType::AT_CLOTHING: + { + if (gAgentWearables.areWearablesLoaded()) + { + if (!cb && do_update) + { + cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); + } + LLWearableType::EType type = item_to_wear->getWearableType(); + S32 wearable_count = gAgentWearables.getWearableCount(type); + if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type)) + { + LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), + wearable_count-1); + removeCOFItemLinks(item_id, cb); + } + + items_to_link.push_back(item_to_wear); + } + } + break; - addCOFItemLink(item_to_wear, do_update, cb); - break; - case LLAssetType::AT_OBJECT: - rez_attachment(item_to_wear, NULL, replace); - break; - default: return false;; - } + case LLAssetType::AT_BODYPART: + { + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + // Remove the existing wearables of the same type. + // Remove existing body parts anyway because we must not be able to wear e.g. two skins. + removeCOFLinksOfType(item_to_wear->getWearableType()); + if (!cb && do_update) + { + cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); + } + items_to_link.push_back(item_to_wear); + } + break; + + case LLAssetType::AT_OBJECT: + { + rez_attachment(item_to_wear, NULL, replace); + } + break; - return true; + default: continue; + } + } + + // Batch up COF link creation - more efficient if using AIS. + if (items_to_link.size()) + { + link_inventory_array(getCOF(), items_to_link, cb); + } +} + +void LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, + bool do_update, + bool replace, + LLPointer cb) +{ + uuid_vec_t ids; + ids.push_back(item_id_to_wear); + wearItemsOnAvatar(ids, do_update, replace, cb); } // Update appearance from outfit folder. @@ -1555,12 +1630,12 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) // deactivate all gestures in the outfit folder LLInventoryModel::item_array_t gest_items; getDescendentsOfAssetType(cat_id, gest_items, LLAssetType::AT_GESTURE, true); - for(S32 i = 0; i < gest_items.size(); ++i) + for(S32 i = 0; i < gest_items.size(); ++i) { - LLViewerInventoryItem* gest_item = gest_items.at(i); - if (LLGestureMgr::instance().isGestureActive(gest_item->getLinkedUUID())) + LLViewerInventoryItem *gest_item = gest_items[i]; + if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) ) { - LLGestureMgr::instance().deactivateGesture(gest_item->getLinkedUUID()); + LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() ); } } } @@ -1589,6 +1664,58 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds gInventory.notifyObservers(); } +void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id, + bool include_folder_links, LLPointer cb) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + LLSD contents = LLSD::emptyArray(); + gInventory.getDirectDescendentsOf(src_id, cats, items); + LL_INFOS() << "copying " << items->size() << " items" << LL_ENDL; + for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); + iter != items->end(); + ++iter) + { + const LLViewerInventoryItem* item = (*iter); + switch (item->getActualType()) + { + case LLAssetType::AT_LINK: + { + LL_DEBUGS("Avatar") << "linking inventory item " << item->getName() << LL_ENDL; + //getActualDescription() is used for a new description + //to propagate ordering information saved in descriptions of links + LLSD item_contents; + item_contents["name"] = item->getName(); + item_contents["desc"] = item->getActualDescription(); + item_contents["linked_id"] = item->getLinkedUUID(); + item_contents["type"] = LLAssetType::AT_LINK; + contents.append(item_contents); + break; + } + case LLAssetType::AT_LINK_FOLDER: + { + LLViewerInventoryCategory *catp = item->getLinkedCategory(); + if (catp && include_folder_links) + { + LL_DEBUGS("Avatar") << "linking inventory folder " << item->getName() << LL_ENDL; + LLSD base_contents; + base_contents["name"] = catp->getName(); + base_contents["desc"] = ""; // categories don't have descriptions. + base_contents["linked_id"] = catp->getLinkedUUID(); + base_contents["type"] = LLAssetType::AT_LINK_FOLDER; + contents.append(base_contents); + } + break; + } + default: + { + // Linux refuses to compile unless all possible enums are handled. Really, Linux? + break; + } + } + } + slam_inventory_folder(dst_id, contents, cb); +} // Copy contents of src_id to dst_id. void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, LLPointer cb) @@ -1602,6 +1729,8 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_array_t* items, LLPointer cb) { + LL_INFOS() << "copying " << items->size() << " items" << LL_ENDL; + LLInventoryObject::const_object_list_t link_array; for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); iter != items->end(); ++iter) @@ -1611,14 +1740,8 @@ void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_arr { case LLAssetType::AT_LINK: { - //LLInventoryItem::getDescription() is used for a new description - //to propagate ordering information saved in descriptions of links - link_inventory_item(gAgent.getID(), - item->getLinkedUUID(), - dst_id, - item->getName(), - item->getActualDescription(), - LLAssetType::AT_LINK, cb); + LL_DEBUGS("Avatar") << "linking inventory item " << item->getName() << LL_ENDL; + link_array.push_back(LLConstPointer(item)); break; } case LLAssetType::AT_LINK_FOLDER: @@ -1627,12 +1750,8 @@ void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_arr // Skip copying outfit links. if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT) { - link_inventory_item(gAgent.getID(), - item->getLinkedUUID(), - dst_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK_FOLDER, cb); + LL_DEBUGS("Avatar") << "linking inventory folder " << item->getName() << LL_ENDL; + link_array.push_back(LLConstPointer(item)); } break; } @@ -1643,16 +1762,11 @@ void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_arr { if(!item->getPermissions().allowCopyBy(gAgent.getID())) { - link_inventory_item(gAgent.getID(), - item->getUUID(), - dst_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK, cb); + link_array.push_back(LLConstPointer(item)); } else { - LL_INFOS() << "copying inventory item " << item->getName() << LL_ENDL; + LL_DEBUGS("Avatar") << "copying inventory item " << item->getName() << LL_ENDL; copy_inventory_item(gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), @@ -1667,6 +1781,10 @@ void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_arr break; } } + if (!link_array.empty()) + { + link_inventory_array(dst_id, link_array, cb); + } } BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id) @@ -1731,26 +1849,13 @@ bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) // static bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id) { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; + if (gAgentWearables.isCOFChangeInProgress()) + { + return false; + } + LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false); - gInventory.collectDescendentsIf(outfit_cat_id, - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - is_worn, - /*follow_folder_links=*/ true); - - if (items.size()) return true; - - // Is there an active gesture in outfit_cat_id? - items.clear(); - LLIsType is_gesture(LLAssetType::AT_GESTURE); - gInventory.collectDescendentsIf(outfit_cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_gesture, /*follow_folder_links=*/ true); - for(S32 i = 0; i < items.size(); ++i) - if (LLGestureMgr::instance().isGestureActive(items.at(i)->getLinkedUUID())) - return true; - return false; + return gInventory.hasMatchingDirectDescendent(outfit_cat_id, is_worn, true); } // static @@ -1769,6 +1874,7 @@ bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id) items, LLInventoryModel::EXCLUDE_TRASH, not_worn); + return items.size() > 0; } @@ -1798,10 +1904,54 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) items, LLInventoryModel::EXCLUDE_TRASH, is_worn); + return items.size() > 0; } -void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) +// Moved from LLWearableList::ContextMenu for wider utility. +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) +{ + // TODO: investigate wearables may not be loaded at this point EXT-8231 + + U32 n_objects = 0; + U32 n_clothes = 0; + + // Count given clothes (by wearable type) and objects. + for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if (!item) + { + return false; + } + + if (item->getType() == LLAssetType::AT_OBJECT) + { + ++n_objects; + } + else if (item->getType() == LLAssetType::AT_CLOTHING) + { + ++n_clothes; + } + else + { + LL_WARNS() << "Unexpected wearable type" << LL_ENDL; + return false; + } + } + + // Check whether we can add all the objects. + if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects)) + { + return false; + } + + // Check whether we can add all the clothes. + U32 sum_clothes = n_clothes + gAgentWearables.getClothingLayerCount(); + return sum_clothes <= LLAgentWearables::MAX_CLOTHING_LAYERS; +} + +void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer cb) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; @@ -1817,7 +1967,7 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) LLViewerInventoryCategory* catp = item->getLinkedCategory(); if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) { - gInventory.purgeObject(item->getUUID()); + remove_inventory_item(item->getUUID(), cb); } } } @@ -1836,19 +1986,8 @@ void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_lin continue; if (item->getIsLinkType()) { -#if 0 - if (keep_items && keep_items->find(item) != LLInventoryModel::item_array_t::FAIL) - { - LL_INFOS() << "preserved item" << LL_ENDL; - } - else - { - gInventory.purgeObject(item->getUUID()); - } -#else - gInventory.purgeObject(item->getUUID()); + remove_inventory_item(item->getUUID(), NULL); } -#endif } } @@ -1860,7 +1999,7 @@ void LLAppearanceMgr::purgeItems(const LLInventoryModel::item_array_t& items) const LLViewerInventoryItem* pItem = *itItem; if (pItem->getIsLinkType()) { - gInventory.purgeObject(pItem->getUUID()); + remove_inventory_object(pItem->getUUID(), NULL); } } } @@ -1875,50 +2014,7 @@ void LLAppearanceMgr::purgeItemsOfType(LLAssetType::EType asset_type) const LLInventoryItem* pItem = *itItem; if ( (pItem->getIsLinkType()) && (asset_type == pItem->getType()) ) { - gInventory.purgeObject(pItem->getUUID()); - } - } -} - -void LLAppearanceMgr::syncCOF(const LLInventoryModel::item_array_t& items, - LLInventoryModel::item_array_t& items_to_add, LLInventoryModel::item_array_t& items_to_remove) -{ - const LLUUID idCOF = getCOF(); - LLInventoryModel::item_array_t cur_cof_items, new_cof_items = items; - - // Grab the current COF contents - LLInventoryModel::cat_array_t cats; - gInventory.collectDescendents(getCOF(), cats, cur_cof_items, LLInventoryModel::EXCLUDE_TRASH); - - // Purge everything in cur_cof_items that isn't part of new_cof_items - for (S32 idxCurItem = 0, cntCurItem = cur_cof_items.size(); idxCurItem < cntCurItem; idxCurItem++) - { - LLViewerInventoryItem* pItem = cur_cof_items.at(idxCurItem); - if (std::find_if(new_cof_items.begin(), new_cof_items.end(), RlvPredIsEqualOrLinkedItem(pItem)) == new_cof_items.end()) - { - // Item doesn't exist in new_cof_items => purge (if it's a link) - if ( (pItem->getIsLinkType()) && - (LLAssetType::AT_LINK_FOLDER != pItem->getActualType()) && - (items_to_remove.end() == std::find(items_to_remove.begin(), items_to_remove.end(), pItem)) ) - { - items_to_remove.push_back(pItem); - } - } - else - { - // Item exists in new_cof_items => remove *all* occurances in new_cof_items (removes duplicate COF links to this item as well) - new_cof_items.erase( - std::remove_if(new_cof_items.begin(), new_cof_items.end(), RlvPredIsEqualOrLinkedItem(pItem)), new_cof_items.end()); - } - } - - // Whatever remains in new_cof_items will need to have a link created - for (S32 idxNewItem = 0, cntNewItem = new_cof_items.size(); idxNewItem < cntNewItem; idxNewItem++) - { - LLViewerInventoryItem* pItem = new_cof_items.at(idxNewItem); - if (items_to_add.end() == std::find(items_to_add.begin(), items_to_add.end(), pItem)) - { - items_to_add.push_back(pItem); + remove_inventory_object(pItem->getUUID(), NULL); } } } @@ -1927,55 +2023,44 @@ void LLAppearanceMgr::syncCOF(const LLInventoryModel::item_array_t& items, // Keep the last N wearables of each type. For viewer 2.0, N is 1 for // both body parts and clothing items. void LLAppearanceMgr::filterWearableItems( - LLInventoryModel::item_array_t& items, S32 max_per_type) + LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total) { - // Divvy items into arrays by wearable type. - std::vector items_by_type(LLWearableType::WT_COUNT); - divvyWearablesByType(items, items_by_type); + // Restrict by max total items first. + if ((max_total > 0) && (items.size() > max_total)) + { + LLInventoryModel::item_array_t items_to_keep; + for (S32 i=0; i 0) + { + // Divvy items into arrays by wearable type. + std::vector items_by_type(LLWearableType::WT_COUNT); + divvyWearablesByType(items, items_by_type); + + // rebuild items list, retaining the last max_per_type of each array + items.clear(); + for (S32 i=0; i cb) -{ - for (S32 i=0; igetLinkedUUID(), - cat_uuid, - item->getName(), - item->getActualDescription(), - LLAssetType::AT_LINK, - cb); - - const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); - const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; -#ifndef LL_RELEASE_FOR_DOWNLOAD - LL_DEBUGS("Avatar") << self_av_string() << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << LL_ENDL; -#endif - } -} - - //void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) // [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0b) | Added: RLVa-1.2.0b void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) @@ -1985,6 +2070,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) getDescendentsOfAssetType(category, wear_items_new, LLAssetType::AT_CLOTHING, true); getDescendentsOfAssetType(category, obj_items_new, LLAssetType::AT_OBJECT, true); getDescendentsOfAssetType(category, gest_items_new, LLAssetType::AT_GESTURE, true); + updateCOF(body_items_new, wear_items_new, obj_items_new, gest_items_new, append, category); } @@ -1992,7 +2078,7 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, LLInventoryModel::item_array_t& wear_items_new, LLInventoryModel::item_array_t& obj_items_new, LLInventoryModel::item_array_t& gest_items_new, - bool append /*=false*/, const LLUUID& idOutfit /*=LLUUID::null*/) + bool append /*=false*/, const LLUUID& category /*=LLUUID::null*/) // [/RLVa:KB] { // LLViewerInventoryCategory *pcat = gInventory.getCategory(category); @@ -2008,7 +2094,7 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, if (!append) { LLInventoryModel::item_array_t gest_items; - getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); + getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE); for(S32 i = 0; i < gest_items.size(); ++i) { LLViewerInventoryItem *gest_item = gest_items.at(i); @@ -2025,8 +2111,8 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, // required parts are missing. // Preserve body parts from COF if appending. LLInventoryModel::item_array_t body_items; - getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false); -// getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false); + getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART); +// getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART); // [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b // Filter out any new body parts that can't be worn before adding them if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) @@ -2038,21 +2124,21 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, // reverse(body_items.begin(), body_items.end()); // Reduce body items to max of one per type. removeDuplicateItems(body_items); - filterWearableItems(body_items, 1); + filterWearableItems(body_items, 1, 0); // - Wearables: include COF contents only if appending. LLInventoryModel::item_array_t wear_items; if (append) - getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false); + getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING); // [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b else if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) { // Make sure that all currently locked clothing layers remain in COF when replacing - getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false); + getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING); wear_items.erase(std::remove_if(wear_items.begin(), wear_items.end(), rlvPredCanRemoveItem), wear_items.end()); } // [/RLVa:KB] -// getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false); +// getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING); // [RLVa:KB] - Checked: 2010-03-19 (RLVa-1.2.0c) | Modified: RLVa-1.2.0b // Filter out any new wearables that can't be worn before adding them if ( (rlv_handler_t::isEnabled()) && (gRlvWearableLocks.hasLockedWearableType(RLV_LOCK_ANY)) ) @@ -2064,23 +2150,21 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, // [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e removeDuplicateWearableItemsByAssetID(wear_items); // [/SL:KB] - filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); + filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS); - // // - Attachments: include COF contents only if appending. - // LLInventoryModel::item_array_t obj_items; if (append) - getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false); + getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT); // [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b else if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) { // Make sure that all currently locked attachments remain in COF when replacing - getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false); + getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT); obj_items.erase(std::remove_if(obj_items.begin(), obj_items.end(), rlvPredCanRemoveItem), obj_items.end()); } // [/RLVa:KB] -// getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false); +// getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT); // [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Modified: RLVa-1.2.0b // Filter out any new attachments that can't be worn before adding them if ( (rlv_handler_t::isEnabled()) && (gRlvAttachmentLocks.hasLockedAttachmentPoint(RLV_LOCK_ANY)) ) @@ -2094,8 +2178,8 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, // LLInventoryModel::item_array_t gest_items; if (append) - getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); -// getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false); + getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE); +// getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE); // [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0z) | Added: RLVa-1.2.0b gest_items.insert(gest_items.end(), gest_items_new.begin(), gest_items_new.end()); // [/RLVa:KB] @@ -2108,50 +2192,56 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new, std::copy(obj_items.begin(), obj_items.end(), std::back_inserter(all_items)); std::copy(gest_items.begin(), gest_items.end(), std::back_inserter(all_items)); -// [SL:KB] - // Synchronize COF - // -> it's possible that we don't link to any new items in which case 'link_waiter' fires when it goes out of scope below - LLInventoryModel::item_array_t items_add, items_remove; - syncCOF(all_items, items_add, items_remove); -// [/SL:KB] + // Find any wearables that need description set to enforce ordering. + desc_map_t desc_map; + getWearableOrderingDescUpdates(wear_items, desc_map); // Will link all the above items. - LLPointer link_waiter = new LLUpdateAppearanceOnDestroy; -// [SL:KB] - Checked: 2013-03-05 (RLVa-1.4.8) - linkAll(cof, items_add, link_waiter); -// [/SL:KB] -// linkAll(cof,all_items,link_waiter); + // link_waiter enforce flags are false because we've already fixed everything up in updateCOF(). + LLPointer link_waiter = new LLUpdateAppearanceOnDestroy(false,false); + LLSD contents = LLSD::emptyArray(); - // Add link to outfit if category is an outfit. -// [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f - if ( (!append) && (idOutfit.notNull()) ) + for (LLInventoryModel::item_array_t::const_iterator it = all_items.begin(); + it != all_items.end(); ++it) { - createBaseOutfitLink(idOutfit, link_waiter); - } -// [/SL:KB] -// if (!append) -// { -// createBaseOutfitLink(category, link_waiter); -// } -// - // Remove current COF contents. Have to do this after creating - // the link_waiter so links can be followed for any items that get - // carried over (e.g. keeping old shape if the new outfit does not - // contain one) -// [SL:KB] - purgeItems(items_remove); + LLSD item_contents; + LLInventoryItem *item = *it; - bool keep_outfit_links = append; - if (!keep_outfit_links) + std::string desc; + desc_map_t::const_iterator desc_iter = desc_map.find(item->getUUID()); + if (desc_iter != desc_map.end()) + { + desc = desc_iter->second; + LL_DEBUGS("Avatar") << item->getName() << " overriding desc to: " << desc + << " (was: " << item->getActualDescription() << ")" << LL_ENDL; + } + else + { + desc = item->getActualDescription(); + } + + item_contents["name"] = item->getName(); + item_contents["desc"] = desc; + item_contents["linked_id"] = item->getLinkedUUID(); + item_contents["type"] = LLAssetType::AT_LINK; + contents.append(item_contents); + } + const LLUUID& base_id = append ? getBaseOutfitUUID() : category; + LLViewerInventoryCategory *base_cat = gInventory.getCategory(base_id); + if (base_cat) { - purgeItemsOfType(LLAssetType::AT_LINK_FOLDER); + LLSD base_contents; + base_contents["name"] = base_cat->getName(); + base_contents["desc"] = ""; + base_contents["linked_id"] = base_cat->getLinkedUUID(); + base_contents["type"] = LLAssetType::AT_LINK_FOLDER; + contents.append(base_contents); } - - gInventory.notifyObservers(); -// [/SL:KB] -// bool keep_outfit_links = append; -// purgeCategory(cof, keep_outfit_links, &all_items); -// gInventory.notifyObservers(); + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents); + } + slam_inventory_folder(getCOF(), contents, link_waiter); LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL; } @@ -2171,23 +2261,23 @@ void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointergetPreferredType() == LLFolderType::FT_OUTFIT) { - link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "", - LLAssetType::AT_LINK_FOLDER, link_waiter); + link_inventory_object(cof, catp, link_waiter); new_outfit_name = catp->getName(); } updatePanelOutfitName(new_outfit_name); } -void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append) +void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder) { - LL_DEBUGS() << "updateAgentWearables()" << LL_ENDL; + LL_DEBUGS("Avatar") << "updateAgentWearables()" << LL_ENDL; LLInventoryItem::item_array_t items; std::vector< LLViewerWearable* > wearables; + wearables.reserve(32); // [RLVa:KB] - Checked: 2011-03-31 (RLVa-1.3.0f) | Added: RLVa-1.3.0f uuid_vec_t idsCurrent; LLInventoryModel::item_array_t itemsNew; if (rlv_handler_t::isEnabled()) @@ -2266,10 +2356,15 @@ void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, boo if(wearables.size() > 0) { - gAgentWearables.setWearableOutfit(items, wearables, !append); + gAgentWearables.setWearableOutfit(items, wearables); } } +S32 LLAppearanceMgr::countActiveHoldingPatterns() +{ + return LLWearableHoldingPattern::countActive(); +} + static void remove_non_link_items(LLInventoryModel::item_array_t &items) { LLInventoryModel::item_array_t pruned_items; @@ -2317,18 +2412,19 @@ void item_array_diff(LLInventoryModel::item_array_t& full_list, S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id, LLAssetType::EType type, - S32 max_items, - LLInventoryModel::item_array_t& items_to_kill) + S32 max_items_per_type, + S32 max_items_total, + LLInventoryObject::object_list_t& items_to_kill) { S32 to_kill_count = 0; LLInventoryModel::item_array_t items; - getDescendentsOfAssetType(cat_id, items, type, false); + getDescendentsOfAssetType(cat_id, items, type); LLInventoryModel::item_array_t curr_items = items; removeDuplicateItems(items); - if (max_items > 0) + if (max_items_per_type > 0 || max_items_total > 0) { - filterWearableItems(items, max_items); + filterWearableItems(items, max_items_per_type, max_items_total); } LLInventoryModel::item_array_t kill_items; item_array_diff(curr_items,items,kill_items); @@ -2336,40 +2432,39 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id, it != kill_items.end(); ++it) { - items_to_kill.push_back(*it); + items_to_kill.push_back(LLPointer(*it)); to_kill_count++; } return to_kill_count; } - -void LLAppearanceMgr::enforceItemRestrictions() + +void LLAppearanceMgr::findAllExcessOrDuplicateItems(const LLUUID& cat_id, + LLInventoryObject::object_list_t& items_to_kill) { - S32 purge_count = 0; - LLInventoryModel::item_array_t items_to_kill; - - purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART, - 1, items_to_kill); - purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING, - LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill); - purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT, - -1, items_to_kill); + findExcessOrDuplicateItems(cat_id,LLAssetType::AT_BODYPART, + 1, 0, items_to_kill); + findExcessOrDuplicateItems(cat_id,LLAssetType::AT_CLOTHING, + 0, LLAgentWearables::MAX_CLOTHING_LAYERS, items_to_kill); + findExcessOrDuplicateItems(cat_id,LLAssetType::AT_OBJECT, + 0, 0, items_to_kill); +} +void LLAppearanceMgr::enforceCOFItemRestrictions(LLPointer cb) +{ + LLInventoryObject::object_list_t items_to_kill; + findAllExcessOrDuplicateItems(getCOF(), items_to_kill); if (items_to_kill.size()>0) { - for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin(); - it != items_to_kill.end(); - ++it) - { - LLViewerInventoryItem *item = *it; - LL_DEBUGS("Avatar") << self_av_string() << "purging duplicate or excess item " << item->getName() << LL_ENDL; - gInventory.purgeObject(item->getUUID()); - } - gInventory.notifyObservers(); + // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but + // this should catch anything that gets through. + remove_inventory_items(items_to_kill, cb); } } -void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) +void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions, + bool enforce_ordering, + nullary_func_t post_update_func) { if (mIsInUpdateAppearanceFromCOF) { @@ -2377,19 +2472,43 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) return; } + LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL; + + if (enforce_item_restrictions) + { + // The point here is just to call + // updateAppearanceFromCOF() again after excess items + // have been removed. That time we will set + // enforce_item_restrictions to false so we don't get + // caught in a perpetual loop. + LLPointer cb( + new LLUpdateAppearanceOnDestroy(false, enforce_ordering, post_update_func)); + enforceCOFItemRestrictions(cb); + return; + } + + if (enforce_ordering) + { + //checking integrity of the COF in terms of ordering of wearables, + //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state) + + // As with enforce_item_restrictions handling above, we want + // to wait for the update callbacks, then (finally!) call + // updateAppearanceFromCOF() with no additional COF munging needed. + LLPointer cb( + new LLUpdateAppearanceOnDestroy(false, false, post_update_func)); + updateClothingOrderingInfo(LLUUID::null, cb); + return; + } + + if (!validateClothingOrderingInfo()) + { + LL_WARNS() << "Clothing ordering error" << LL_ENDL; + } + BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF); selfStartPhase("update_appearance_from_cof"); - LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL; - - //checking integrity of the COF in terms of ordering of wearables, - //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state) - updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering); - - // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but - // this should catch anything that gets through. - enforceItemRestrictions(); - // update dirty flag to see if the state of the COF matches // the saved outfit stored as a folder link updateIsDirty(); @@ -2399,13 +2518,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) { requestServerAppearanceUpdate(); } - // DRANO really should wait for the appearance message to set this. - // verify that deleting this line doesn't break anything. - //gAgentAvatarp->setIsUsingServerBakes(gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion()); - - //dumpCat(getCOF(),"COF, start"); - bool follow_folder_links = false; LLUUID current_outfit_id = getCOF(); // Find all the wearables that are in the COF's subtree. @@ -2413,7 +2526,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) LLInventoryModel::item_array_t wear_items; LLInventoryModel::item_array_t obj_items; LLInventoryModel::item_array_t gest_items; - getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links); + getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items); // Get rid of non-links in case somehow the COF was corrupted. remove_non_link_items(wear_items); remove_non_link_items(obj_items); @@ -2423,7 +2536,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) removeDuplicateItems(wear_items); removeDuplicateItems(obj_items); removeDuplicateItems(gest_items); - filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); + filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS); // [/SL:KB] // [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e // Wearing two wearables that share the same asset causes some issues @@ -2433,36 +2546,13 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) dumpItemArray(wear_items,"asset_dump: wear_item"); dumpItemArray(obj_items,"asset_dump: obj_item"); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-22 (Catznip-2.2) - // Update attachments to match those requested. - if (isAgentAvatarValid()) + LLViewerInventoryCategory *cof = gInventory.getCategory(current_outfit_id); + if (!gInventory.isCategoryComplete(current_outfit_id)) { - // Include attachments which should be in COF but don't have their link created yet - uuid_vec_t::iterator itPendingAttachLink = mPendingAttachLinks.begin(); - while (itPendingAttachLink != mPendingAttachLinks.end()) - { - const LLUUID& idItem = *itPendingAttachLink; - if ( (!gAgentAvatarp->isWearingAttachment(idItem)) || (isLinkInCOF(idItem)) ) - { - itPendingAttachLink = mPendingAttachLinks.erase(itPendingAttachLink); - continue; - } - - LLViewerInventoryItem* pItem = gInventory.getItem(idItem); - if (pItem) - { - obj_items.push_back(pItem); - } - - ++itPendingAttachLink; - } - - // Don't remove attachments until avatar is fully loaded (should reduce random attaching/detaching/reattaching at log-on) - LL_DEBUGS("Avatar") << self_av_string() << "Updating " << obj_items.size() << " attachments" << LL_ENDL; - LLAgentWearables::userUpdateAttachments(obj_items, !gAgentAvatarp->isFullyLoaded()); + LL_WARNS() << "COF info is not complete. Version " << cof->getVersion() + << " descendent_count " << cof->getDescendentCount() + << " viewer desc count " << cof->getViewerDescendentCount() << LL_ENDL; } -// [/SL:KB] - if(!wear_items.size()) { LLNotificationsUtil::add("CouldNotPutOnOutfit"); @@ -2473,9 +2563,11 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) sortItemsByActualDescription(wear_items); + LL_DEBUGS("Avatar") << "HP block starts" << LL_ENDL; + LLTimer hp_block_timer; LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; -// holder->setObjItems(obj_items); + holder->setObjItems(obj_items); holder->setGestItems(gest_items); // Note: can't do normal iteration, because if all the @@ -2540,7 +2632,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) } } - selfStartPhase("get_wearables"); + selfStartPhase("get_wearables_2"); for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin(); it != holder->getFoundList().end(); ++it) @@ -2564,20 +2656,21 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) { doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder)); } + post_update_func(); + + LL_DEBUGS("Avatar") << "HP block ends, elapsed " << hp_block_timer.getElapsedTimeF32() << LL_ENDL; } // [SL:KB] - Patch: Appearance-MixedViewers | Checked: 2010-04-02 (Catznip-3.0.0a) | Added: Catznip-2.0.0a -void LLAppearanceMgr::updateAppearanceFromInitialWearables(LLInventoryModel::item_array_t& initial_items) +void LLAppearanceMgr::updateAppearanceFromInitialWearables(LLInventoryObject::const_object_list_t& initial_items) { const LLUUID& idCOF = getCOF(); // Remove current COF contents purgeCategory(idCOF, false); - gInventory.notifyObservers(); - // Create links to new COF contents LLPointer link_waiter = new LLUpdateAppearanceOnDestroy(); - linkAll(idCOF, initial_items, link_waiter); + link_inventory_array(idCOF, initial_items, link_waiter); } // [/SL:KB] @@ -2643,10 +2736,12 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName() << " )" << LL_ENDL; - selfStartPhase("wear_inventory_category_fetch"); - callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, - &LLAppearanceMgr::instance(), - category->getUUID(), copy, append)); + { + selfStartPhase("wear_inventory_category_fetch"); + callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal, + &LLAppearanceMgr::instance(), + category->getUUID(), copy, append)); + } } S32 LLAppearanceMgr::getActiveCopyOperations() const @@ -2806,9 +2901,8 @@ bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventor class LLDeferredCOFLinkObserver: public LLInventoryObserver { public: - LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer cb = NULL, std::string description = ""): + LLDeferredCOFLinkObserver(const LLUUID& item_id, LLPointer cb, const std::string& description): mItemID(item_id), - mDoUpdate(do_update), mCallback(cb), mDescription(description) { @@ -2824,14 +2918,13 @@ public: if (item) { gInventory.removeObserver(this); - LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback); + LLAppearanceMgr::instance().addCOFItemLink(item, mCallback, mDescription); delete this; } } private: const LLUUID mItemID; - bool mDoUpdate; std::string mDescription; LLPointer mCallback; }; @@ -2839,43 +2932,26 @@ private: // BAP - note that this runs asynchronously if the item is not already loaded from inventory. // Dangerous if caller assumes link will exist after calling the function. -void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer cb, const std::string description) +void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, + LLPointer cb, + const std::string description) { const LLInventoryItem *item = gInventory.getItem(item_id); if (!item) { - LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb, description); + LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, cb, description); gInventory.addObserver(observer); } else { - addCOFItemLink(item, do_update, cb, description); + addCOFItemLink(item, cb, description); } } -void modified_cof_cb(const LLUUID& inv_item) +void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, + LLPointer cb, + const std::string description) { - LLAppearanceMgr::instance().updateAppearanceFromCOF(); - - // Start editing the item if previously requested. - gAgentWearables.editWearableIfRequested(inv_item); - - // TODO: camera mode may not be changed if a debug setting is tweaked - if( LLFloaterCustomize::instanceExists() ) - { - // If we're in appearance editing mode, the current tab may need to be refreshed - /*LLSidepanelAppearance *panel = dynamic_cast(LLFloaterSidePanelContainer::getPanel("appearance")); - if (panel) - { - panel->showDefaultSubpart(); - }*/ - LLFloaterCustomize::getInstance()->switchToDefaultSubpart(); - } -} - -void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer cb, const std::string description) -{ - std::string link_description = description; const LLViewerInventoryItem *vitem = dynamic_cast(item); if (!vitem) { @@ -2892,7 +2968,9 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update item_array, LLInventoryModel::EXCLUDE_TRASH); bool linked_already = false; - U32 count = 0; + + LLUUID remove_id; + for (U32 i=0; igetAssetUUID() == vitem->getAssetUUID()) + { + // Only allow one wearable per unique asset + linked_already = true; + } +// [/SL:KB] // Are these links to different items of the same body part // type? If so, new item will replace old. else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type)) { - ++count; - if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) + if (remove_id.isNull() && !gAgentWearables.canAddWearable(wearable_type)) { - gInventory.purgeObject(inv_item->getUUID()); + // MULTI-WEARABLES: make sure we don't go over clothing limits + remove_id = inv_item->getUUID(); } - else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) - { - // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE - gInventory.purgeObject(inv_item->getUUID()); - } -// [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e - else if ( (vitem->getWearableType() == wearable_type) && (vitem->getAssetUUID() == inv_item->getAssetUUID()) ) - { - // Only allow one wearable per unique asset - linked_already = true; - } -// [/SL:KB] } } - if (linked_already) + if (!linked_already) { - if (do_update) - { - LLAppearanceMgr::updateAppearanceFromCOF(); - } - return; - } - else - { - if(do_update && cb.isNull()) + if( remove_id.notNull() ) { - cb = new LLBoostFuncInventoryCallback(modified_cof_cb); + remove_inventory_item(remove_id, cb); } - if (vitem->getIsLinkType()) - { - link_description = vitem->getActualDescription(); - } - link_inventory_item( gAgent.getID(), - vitem->getLinkedUUID(), - getCOF(), - vitem->getName(), - link_description, - LLAssetType::AT_LINK, - cb); + LLViewerInventoryItem *copy_item = new LLViewerInventoryItem; + copy_item->copyViewerItem(vitem); + copy_item->setDescription(description); + link_inventory_object(getCOF(), copy_item, cb); } - return; } LLInventoryModel::item_array_t LLAppearanceMgr::findCOFItemLinks(const LLUUID& item_id) @@ -3004,8 +3061,7 @@ void LLAppearanceMgr::removeAllClothesFromAvatar() dummy, clothing_items, LLInventoryModel::EXCLUDE_TRASH, - is_clothing, - false); + is_clothing); uuid_vec_t item_ids; for (LLInventoryModel::item_array_t::iterator it = clothing_items.begin(); it != clothing_items.end(); ++it) @@ -3049,7 +3105,7 @@ void LLAppearanceMgr::removeAllAttachmentsFromAvatar() removeItemsFromAvatar(ids_to_remove); } -void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id) +void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointer cb) { gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); @@ -3064,12 +3120,17 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id) const LLInventoryItem* item = item_array.at(i).get(); if (item->getIsLinkType() && item->getLinkedUUID() == item_id) { - gInventory.purgeObject(item->getUUID()); + bool immediate_delete = false; + if (item->getType() == LLAssetType::AT_OBJECT) + { + immediate_delete = true; + } + remove_inventory_item(item->getUUID(), cb, immediate_delete); } } } -void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type) +void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, LLPointer cb) { LLFindWearablesOfType filter_wearables_of_type(type); LLInventoryModel::cat_array_t cats; @@ -3082,7 +3143,7 @@ void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type) const LLViewerInventoryItem* item = *it; if (item->getIsLinkType()) // we must operate on links only { - gInventory.purgeObject(item->getUUID()); + remove_inventory_item(item->getUUID(), cb); } } } @@ -3121,7 +3182,7 @@ void LLAppearanceMgr::updateIsDirty() if (base_outfit.notNull()) { - LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK); + LLIsValidItemLink collector; LLInventoryModel::cat_array_t cof_cats; LLInventoryModel::item_array_t cof_items; @@ -3195,7 +3256,7 @@ void LLAppearanceMgr::copyLibraryGestures() // Copy gestures LLUUID lib_gesture_cat_id = - gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE,false,true); + gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false); if (lib_gesture_cat_id.isNull()) { LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL; @@ -3281,9 +3342,27 @@ void appearance_mgr_update_dirty_state() if (LLAppearanceMgr::instanceExists()) { LLAppearanceMgr::getInstance()->updateIsDirty(); + LLAppearanceMgr::getInstance()->setOutfitLocked(false); + gAgentWearables.notifyLoadingFinished(); } } +void update_base_outfit_after_ordering() +{ + LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance(); + + LLPointer dirty_state_updater = + new LLBoostFuncInventoryCallback(no_op_inventory_func, appearance_mgr_update_dirty_state); + + //COF contains only links so we copy to the Base Outfit only links + const LLUUID base_outfit_id = app_mgr.getBaseOutfitUUID(); + bool copy_folder_links = false; + app_mgr.slamCategoryLinks(app_mgr.getCOF(), base_outfit_id, copy_folder_links, dirty_state_updater); + +} + +// Save COF changes - update the contents of the current base outfit +// to match the current COF. Fails if no current base outfit is set. bool LLAppearanceMgr::updateBaseOutfit() { if (isOutfitLocked()) @@ -3299,17 +3378,13 @@ bool LLAppearanceMgr::updateBaseOutfit() const LLUUID base_outfit_id = getBaseOutfitUUID(); if (base_outfit_id.isNull()) return false; + LL_DEBUGS("Avatar") << "saving cof to base outfit " << base_outfit_id << LL_ENDL; - updateClothingOrderingInfo(); - - // in a Base Outfit we do not remove items, only links - purgeCategory(base_outfit_id, false); - - LLPointer dirty_state_updater = - new LLBoostFuncInventoryCallback(no_op_inventory_func, appearance_mgr_update_dirty_state); - - //COF contains only links so we copy to the Base Outfit only links - shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater); + LLPointer cb = + new LLBoostFuncInventoryCallback(no_op_inventory_func, update_base_outfit_after_ordering); + // Really shouldn't be needed unless there's a race condition - + // updateAppearanceFromCOF() already calls updateClothingOrderingInfo. + updateClothingOrderingInfo(LLUUID::null, cb); return true; } @@ -3379,165 +3454,181 @@ struct WearablesOrderComparator U32 mControlSize; }; -void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base_outfit_ordering) +void LLAppearanceMgr::getWearableOrderingDescUpdates(LLInventoryModel::item_array_t& wear_items, + desc_map_t& desc_map) { - if (cat_id.isNull()) - { - cat_id = getCOF(); - if (update_base_outfit_ordering) - { - const LLUUID base_outfit_id = getBaseOutfitUUID(); - if (base_outfit_id.notNull()) - { - updateClothingOrderingInfo(base_outfit_id,false); - } - } - } - - // COF is processed if cat_id is not specified - LLInventoryModel::item_array_t wear_items; - getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING, false); - wearables_by_type_t items_by_type(LLWearableType::WT_COUNT); divvyWearablesByType(wear_items, items_by_type); - bool inventory_changed = false; for (U32 type = LLWearableType::WT_SHIRT; type < LLWearableType::WT_COUNT; type++) { U32 size = items_by_type[type].size(); if (!size) continue; - + //sinking down invalid items which need reordering std::sort(items_by_type[type].begin(), items_by_type[type].end(), WearablesOrderComparator((LLWearableType::EType) type)); - + //requesting updates only for those links which don't have "valid" descriptions for (U32 i = 0; i < size; i++) { LLViewerInventoryItem* item = items_by_type[type][i]; if (!item) continue; - + std::string new_order_str = build_order_string((LLWearableType::EType)type, i); if (new_order_str == item->getActualDescription()) continue; - - item->setDescription(new_order_str); - item->setComplete(TRUE); - item->updateServer(FALSE); - gInventory.updateItem(item); - - inventory_changed = true; + + desc_map[item->getUUID()] = new_order_str; } } - - //*TODO do we really need to notify observers? - if (inventory_changed) gInventory.notifyObservers(); } -// This is intended for use with HTTP Clients/Responders, but is not -// specifically coupled with those classes. -class LLHTTPRetryPolicy: public LLThreadSafeRefCount +bool LLAppearanceMgr::validateClothingOrderingInfo(LLUUID cat_id) { -public: - LLHTTPRetryPolicy() {} - virtual ~LLHTTPRetryPolicy() {} - virtual bool shouldRetry(U32 status, F32& seconds_to_wait) = 0; -}; - -// Example of simplest possible policy, not necessarily recommended. -class LLAlwaysRetryImmediatelyPolicy: public LLHTTPRetryPolicy -{ -public: - LLAlwaysRetryImmediatelyPolicy() {} - bool shouldRetry(U32 status, F32& seconds_to_wait) - { - seconds_to_wait = 0.0; - return true; - } -}; - -// Very general policy with geometric back-off after failures, -// up to a maximum delay, and maximum number of retries. -class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy -{ -public: - LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries): - mMinDelay(min_delay), - mMaxDelay(max_delay), - mBackoffFactor(backoff_factor), - mMaxRetries(max_retries), - mDelay(min_delay), - mRetryCount(0) + // COF is processed if cat_id is not specified + if (cat_id.isNull()) { + cat_id = getCOF(); } - bool shouldRetry(U32 status, F32& seconds_to_wait) + LLInventoryModel::item_array_t wear_items; + getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING); + + // Identify items for which desc needs to change. + desc_map_t desc_map; + getWearableOrderingDescUpdates(wear_items, desc_map); + + for (desc_map_t::const_iterator it = desc_map.begin(); + it != desc_map.end(); ++it) { - seconds_to_wait = mDelay; - mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay); - mRetryCount++; - return (mRetryCount<=mMaxRetries); + const LLUUID& item_id = it->first; + const std::string& new_order_str = it->second; + LLViewerInventoryItem *item = gInventory.getItem(item_id); + LL_WARNS() << "Order validation fails: " << item->getName() + << " needs to update desc to: " << new_order_str + << " (from: " << item->getActualDescription() << ")" << LL_ENDL; + } + + return desc_map.size() == 0; +} + +void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, + LLPointer cb) +{ + // COF is processed if cat_id is not specified + if (cat_id.isNull()) + { + cat_id = getCOF(); } -private: - F32 mMinDelay; // delay never less than this value - F32 mMaxDelay; // delay never exceeds this value - F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay. - U32 mMaxRetries; // maximum number of times shouldRetry will return true. - F32 mDelay; // current delay. - U32 mRetryCount; // number of times shouldRetry has been called. -}; + LLInventoryModel::item_array_t wear_items; + getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING); + + // Identify items for which desc needs to change. + desc_map_t desc_map; + getWearableOrderingDescUpdates(wear_items, desc_map); + + for (desc_map_t::const_iterator it = desc_map.begin(); + it != desc_map.end(); ++it) + { + LLSD updates; + const LLUUID& item_id = it->first; + const std::string& new_order_str = it->second; + LLViewerInventoryItem *item = gInventory.getItem(item_id); + LL_DEBUGS("Avatar") << item->getName() << " updating desc to: " << new_order_str + << " (was: " << item->getActualDescription() << ")" << LL_ENDL; + updates["desc"] = new_order_str; + update_inventory_item(item_id,updates,cb); + } + +} extern AIHTTPTimeoutPolicy requestAgentUpdateAppearance_timeout; class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::ResponderWithResult { + LOG_CLASS(RequestAgentUpdateAppearanceResponder); + + friend class LLAppearanceMgr; + public: virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return requestAgentUpdateAppearance_timeout; } RequestAgentUpdateAppearanceResponder(); virtual ~RequestAgentUpdateAppearanceResponder(); +private: + // Called when sendServerAppearanceUpdate called. May or may not + // trigger a request depending on various bits of state. + void onRequestRequested(); + + // Post the actual appearance request to cap. + void sendRequest(); + + void debugCOF(const LLSD& content); + protected: // Successful completion. /* virtual */ void httpSuccess(); // Error - /*virtual*/ void httpFailure() - { - LL_WARNS() << "appearance update request failed, " << dumpResponse() << LL_ENDL; - onFailure(mStatus); - } + /*virtual*/ void httpFailure(); - void onFailure(U32 status) - { - F32 seconds_to_wait; - if (mRetryPolicy->shouldRetry(status,seconds_to_wait)) - { - LL_INFOS() << "retrying" << LL_ENDL; - doAfterInterval(boost::bind(&LLAppearanceMgr::requestServerAppearanceUpdate, - LLAppearanceMgr::getInstance(), - LLHTTPClient::ResponderPtr(this)), - seconds_to_wait); - } - else - { - LL_WARNS() << "giving up after too many retries" << LL_ENDL; - } - } + void onFailure(); + void onSuccess(); + S32 mInFlightCounter; + LLTimer mInFlightTimer; LLPointer mRetryPolicy; /*virtual*/ char const* getName(void) const { return "RequestAgentUpdateAppearanceResponder"; } + /*virtual*/ bool needsHeaders() const { return true; } }; RequestAgentUpdateAppearanceResponder::RequestAgentUpdateAppearanceResponder() { - mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10); + bool retry_on_4xx = true; + mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10, retry_on_4xx); + mInFlightCounter = 0; } RequestAgentUpdateAppearanceResponder::~RequestAgentUpdateAppearanceResponder() { } -void LLAppearanceMgr::requestServerAppearanceUpdate(LLHTTPClient::ResponderPtr responder_ptr) +void RequestAgentUpdateAppearanceResponder::onRequestRequested() +{ + // If we have already received an update for this or higher cof version, ignore. + S32 cof_version = LLAppearanceMgr::instance().getCOFVersion(); + S32 last_rcv = gAgentAvatarp->mLastUpdateReceivedCOFVersion; + S32 last_req = gAgentAvatarp->mLastUpdateRequestCOFVersion; + LL_DEBUGS("Avatar") << "cof_version " << cof_version + << " last_rcv " << last_rcv + << " last_req " << last_req + << " in flight " << mInFlightCounter << LL_ENDL; + if ((mInFlightCounter>0) && (mInFlightTimer.hasExpired())) + { + LL_WARNS("Avatar") << "in flight timer expired, resetting " << LL_ENDL; + mInFlightCounter = 0; + } + if (cof_version < last_rcv) + { + LL_DEBUGS("Avatar") << "Have already received update for cof version " << last_rcv + << " will not request for " << cof_version << LL_ENDL; + return; + } + if (mInFlightCounter>0 && last_req >= cof_version) + { + LL_DEBUGS("Avatar") << "Request already in flight for cof version " << last_req + << " will not request for " << cof_version << LL_ENDL; + return; + } + + // Actually send the request. + LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL; + mRetryPolicy->reset(); + sendRequest(); +} + +void RequestAgentUpdateAppearanceResponder::sendRequest() { if (gAgentAvatarp->isEditingAppearance()) { @@ -3548,8 +3639,7 @@ void LLAppearanceMgr::requestServerAppearanceUpdate(LLHTTPClient::ResponderPtr r if (!gAgent.getRegion()) { LL_WARNS() << "Region not set, cannot request server appearance update" << LL_ENDL; - return; //Avoid a crash here. RequestAgentUpdateAppearanceResponder crams requestServerAppearanceUpdate into an event... - //which results in gAgent.getRegion() legitimately returning NULL under some scenarios. + return; } if (gAgent.getRegion()->getCentralBakeVersion()==0) { @@ -3563,18 +3653,332 @@ void LLAppearanceMgr::requestServerAppearanceUpdate(LLHTTPClient::ResponderPtr r } LLSD body; - S32 cof_version = getCOFVersion(); + S32 cof_version = LLAppearanceMgr::instance().getCOFVersion(); body["cof_version"] = cof_version; - LL_DEBUGS("Avatar") << "my_cof_version " << cof_version << LL_ENDL; + LL_DEBUGS("Avatar") << "request url " << url << " my_cof_version " << cof_version << LL_ENDL; + + mInFlightCounter++; + mInFlightTimer.setTimerExpirySec(60.0); + LLHTTPClient::post(url, body, this); + llassert(cof_version >= gAgentAvatarp->mLastUpdateRequestCOFVersion); + gAgentAvatarp->mLastUpdateRequestCOFVersion = cof_version; +} + +void RequestAgentUpdateAppearanceResponder::debugCOF(const LLSD& content) +{ + LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger() + << " ================================= " << LL_ENDL; + std::set ais_items, local_items; + const LLSD& cof_raw = content["cof_raw"]; + for (LLSD::array_const_iterator it = cof_raw.beginArray(); + it != cof_raw.endArray(); ++it) + { + const LLSD& item = *it; + if (item["parent_id"].asUUID() == LLAppearanceMgr::instance().getCOF()) + { + ais_items.insert(item["item_id"].asUUID()); + if (item["type"].asInteger() == 24) // link + { + LL_INFOS("Avatar") << "AIS Link: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << LL_ENDL; + } + else if (item["type"].asInteger() == 25) // folder link + { + LL_INFOS("Avatar") << "AIS Folder link: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << LL_ENDL; + } + else + { + LL_INFOS("Avatar") << "AIS Other: item_id: " << item["item_id"].asUUID() + << " linked_item_id: " << item["asset_id"].asUUID() + << " name: " << item["name"].asString() + << " type: " << item["type"].asInteger() + << LL_ENDL; + } + } + } + LL_INFOS("Avatar") << LL_ENDL; + LL_INFOS("Avatar") << "Local COF, version requested: " << content["observed"].asInteger() + << " ================================= " << LL_ENDL; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), + cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); + for (S32 i=0; igetUUID()); + LL_INFOS("Avatar") << "LOCAL: item_id: " << inv_item->getUUID() + << " linked_item_id: " << inv_item->getLinkedUUID() + << " name: " << inv_item->getName() + << " parent: " << inv_item->getParentUUID() + << LL_ENDL; + } + LL_INFOS("Avatar") << " ================================= " << LL_ENDL; + S32 local_only = 0, ais_only = 0; + for (std::set::iterator it = local_items.begin(); it != local_items.end(); ++it) + { + if (ais_items.find(*it) == ais_items.end()) + { + LL_INFOS("Avatar") << "LOCAL ONLY: " << *it << LL_ENDL; + local_only++; + } + } + for (std::set::iterator it = ais_items.begin(); it != ais_items.end(); ++it) + { + if (local_items.find(*it) == local_items.end()) + { + LL_INFOS("Avatar") << "AIS ONLY: " << *it << LL_ENDL; + ais_only++; + } + } + if (local_only==0 && ais_only==0) + { + LL_INFOS("Avatar") << "COF contents identical, only version numbers differ (req " + << content["observed"].asInteger() + << " rcv " << content["expected"].asInteger() + << ")" << LL_ENDL; + } +} + +/* virtual */ void RequestAgentUpdateAppearanceResponder::httpSuccess() +{ + const LLSD& content = getContent(); + if (!content.isMap()) + { + failureResult(400, "Malformed response contents", content); + return; + } + if (content["success"].asBoolean()) + { + LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; + static LLCachedControl debug_ava_appr_msg(gSavedSettings, "DebugAvatarAppearanceMessage"); + if (debug_ava_appr_msg) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", content); + } + + onSuccess(); + } + else + { + failureResult(400, "Non-success response", content); + } +} + +void RequestAgentUpdateAppearanceResponder::onSuccess() +{ + mInFlightCounter = llmax(mInFlightCounter-1,0); +} + +/*virtual*/ void RequestAgentUpdateAppearanceResponder::httpFailure() +{ + LL_WARNS("Avatar") << "appearance update request failed, status " + << getStatus() << " reason " << getReason() << LL_ENDL; + + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + const LLSD& content = getContent(); + dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content); + debugCOF(content); + } + onFailure(); +} + +void RequestAgentUpdateAppearanceResponder::onFailure() +{ + mInFlightCounter = llmax(mInFlightCounter-1,0); + + F32 seconds_to_wait; + mRetryPolicy->onFailure(getStatus(), getResponseHeaders()); + if (mRetryPolicy->shouldRetry(seconds_to_wait)) + { + LL_INFOS() << "retrying" << LL_ENDL; + doAfterInterval(boost::bind(&RequestAgentUpdateAppearanceResponder::sendRequest,this), + seconds_to_wait); + } + else + { + LL_WARNS() << "giving up after too many retries" << LL_ENDL; + } +} + + +LLSD LLAppearanceMgr::dumpCOF() const +{ + LLSD links = LLSD::emptyArray(); + LLMD5 md5; - //LLHTTPClient::ResponderPtr responder_ptr; + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + gInventory.collectDescendents(getCOF(),cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); + for (S32 i=0; igetUUID()); + md5.update((unsigned char*)item_id.mData, 16); + item["description"] = inv_item->getActualDescription(); + md5.update(inv_item->getActualDescription()); + item["asset_type"] = inv_item->getActualType(); + LLUUID linked_id(inv_item->getLinkedUUID()); + item["linked_id"] = linked_id; + md5.update((unsigned char*)linked_id.mData, 16); + + if (LLAssetType::AT_LINK == inv_item->getActualType()) + { + const LLViewerInventoryItem* linked_item = inv_item->getLinkedItem(); + if (NULL == linked_item) + { + LL_WARNS() << "Broken link for item '" << inv_item->getName() + << "' (" << inv_item->getUUID() + << ") during requestServerAppearanceUpdate" << LL_ENDL; + continue; + } + // Some assets may be 'hidden' and show up as null in the viewer. + //if (linked_item->getAssetUUID().isNull()) + //{ + // LL_WARNS() << "Broken link (null asset) for item '" << inv_item->getName() + // << "' (" << inv_item->getUUID() + // << ") during requestServerAppearanceUpdate" << LL_ENDL; + // continue; + //} + LLUUID linked_asset_id(linked_item->getAssetUUID()); + md5.update((unsigned char*)linked_asset_id.mData, 16); + U32 flags = linked_item->getFlags(); + md5.update(boost::lexical_cast(flags)); + } + else if (LLAssetType::AT_LINK_FOLDER != inv_item->getActualType()) + { + LL_WARNS() << "Non-link item '" << inv_item->getName() + << "' (" << inv_item->getUUID() + << ") type " << (S32) inv_item->getActualType() + << " during requestServerAppearanceUpdate" << LL_ENDL; + continue; + } + links.append(item); + } + LLSD result = LLSD::emptyMap(); + result["cof_contents"] = links; + char cof_md5sum[MD5HEX_STR_SIZE]; + md5.finalize(); + md5.hex_digest(cof_md5sum); + result["cof_md5sum"] = std::string(cof_md5sum); + return result; +} + +void LLAppearanceMgr::requestServerAppearanceUpdate() +{ + mAppearanceResponder->onRequestRequested(); +} + +extern AIHTTPTimeoutPolicy incrementCofVersionResponder_timeout; +class LLIncrementCofVersionResponder : public LLHTTPClient::ResponderWithResult +{ + LOG_CLASS(LLIncrementCofVersionResponder); +public: + LLIncrementCofVersionResponder() : LLHTTPClient::ResponderWithResult() + { + mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5); + } + + virtual ~LLIncrementCofVersionResponder() + { + } + +protected: + virtual void httpSuccess() + { + LL_INFOS() << "Successfully incremented agent's COF." << LL_ENDL; + const LLSD& content = getContent(); + if (!content.isMap()) + { + failureResult(400, "Malformed response contents", content); + return; + } + S32 new_version = content["category"]["version"].asInteger(); + + // cof_version should have increased + llassert(new_version > gAgentAvatarp->mLastUpdateRequestCOFVersion); + + gAgentAvatarp->mLastUpdateRequestCOFVersion = new_version; + } + + virtual void httpFailure() + { + LL_WARNS("Avatar") << "While attempting to increment the agent's cof we got an error " + << dumpResponse() << LL_ENDL; + F32 seconds_to_wait; + mRetryPolicy->onFailure(getStatus(), getResponseHeaders()); + if (mRetryPolicy->shouldRetry(seconds_to_wait)) + { + LL_INFOS() << "retrying" << LL_ENDL; + doAfterInterval(boost::bind(&LLAppearanceMgr::incrementCofVersion, + LLAppearanceMgr::getInstance(), + LLHTTPClient::ResponderPtr(this)), + seconds_to_wait); + } + else + { + LL_WARNS() << "giving up after too many retries" << LL_ENDL; + } + } + +private: + LLPointer mRetryPolicy; +protected: + /*virtual*/ char const* getName(void) const { return "LLIncrementCofVersionResponder"; } + /*virtual*/ bool needsHeaders() const { return true; } +}; + +void LLAppearanceMgr::incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr) +{ + // If we don't have a region, report it as an error + if (gAgent.getRegion() == NULL) + { + LL_WARNS() << "Region not set, cannot request cof_version increment" << LL_ENDL; + return; + } + + std::string url = gAgent.getRegion()->getCapability("IncrementCofVersion"); + if (url.empty()) + { + LL_WARNS() << "No cap for IncrementCofVersion." << LL_ENDL; + return; + } + + LL_INFOS() << "Requesting cof_version be incremented via capability to: " + << url << LL_ENDL; + LLSD body = LLSD::emptyMap(); + if (!responder_ptr.get()) { - responder_ptr = new RequestAgentUpdateAppearanceResponder; + responder_ptr = LLHTTPClient::ResponderPtr(new LLIncrementCofVersionResponder()); } - LLHTTPClient::post(url, body, responder_ptr); - llassert(cof_version >= mLastUpdateRequestCOFVersion); - mLastUpdateRequestCOFVersion = cof_version; + + LLHTTPClient::get(url, body, responder_ptr); +} + +U32 LLAppearanceMgr::getNumAttachmentsInCOF() +{ + const LLUUID cof = getCOF(); + LLInventoryModel::item_array_t obj_items; + getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT); + return obj_items.size(); +} + + +std::string LLAppearanceMgr::getAppearanceServiceURL() const +{ + if (gSavedSettings.getString("AgentAppearanceServiceURL").empty()) + { + return mAppearanceServiceURL; + } + return gSavedSettings.getString("AgentAppearanceServiceURL"); // Singu TODO: Use "DebugAvatarAppearanceServiceURLOverride" } void show_created_outfit(const LLUUID& folder_id, bool show_panel = true) @@ -3585,6 +3989,7 @@ void show_created_outfit(const LLUUID& folder_id, bool show_panel = true) return; } + LL_INFOS("Avatar") << "called" << LL_ENDL; LLSD key; //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process @@ -3605,6 +4010,14 @@ void show_created_outfit(const LLUUID& folder_id, bool show_panel = true) LLAppearanceMgr::getInstance()->updateIsDirty(); gAgentWearables.notifyLoadingFinished(); // New outfit is saved. LLAppearanceMgr::getInstance()->updatePanelOutfitName(""); + + // For SSB, need to update appearance after we add a base outfit + // link, since, the COF version has changed. There is a race + // condition in initial outfit setup which can lead to rez + // failures - SH-3860. + LL_INFOS("Avatar") << "requesting appearance update after createBaseOutfitLink" << LL_ENDL; + LLPointer cb = new LLUpdateAppearanceOnDestroy; + LLAppearanceMgr::getInstance()->createBaseOutfitLink(folder_id, cb); } void scroll_to_folder(const LLUUID& folder_id) @@ -3627,41 +4040,6 @@ void scroll_to_folder(const LLUUID& folder_id) } } -/* virtual */ void RequestAgentUpdateAppearanceResponder::httpSuccess() -{ - const LLSD& content = getContent(); - if (!content.isMap()) - { - failureResult(400, "Malformed response contents", content); - return; - } - if (content["success"].asBoolean()) - { - LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; - static LLCachedControl debug_ava_appr_msg(gSavedSettings, "DebugAvatarAppearanceMessage"); - if (debug_ava_appr_msg) - { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", content); - } - - //onSuccess(); - } - else - { - failureResult(400, "Non-success response", content); - } -} - -std::string LLAppearanceMgr::getAppearanceServiceURL() const -{ - if (gSavedSettings.getString("AgentAppearanceServiceURL").empty()) - { - return mAppearanceServiceURL; - } - return gSavedSettings.getString("AgentAppearanceServiceURL"); // Singu TODO: Use "DebugAvatarAppearanceServiceURLOverride" -} - - class LLBoostFuncInventoryCallbackFireOnce : public LLBoostFuncInventoryCallback { public: @@ -3679,38 +4057,44 @@ private: bool mFired; }; +void LLAppearanceMgr::onOutfitFolderCreated(const LLUUID& folder_id, bool show_panel) +{ + inventory_func_type fire_fn = no_op_inventory_func; + if(show_panel) + fire_fn = boost::bind(&scroll_to_folder,folder_id); + LLPointer cb = + new LLBoostFuncInventoryCallback(fire_fn, + boost::bind(&LLAppearanceMgr::onOutfitFolderCreatedAndClothingOrdered,this,folder_id,show_panel)); + updateClothingOrderingInfo(LLUUID::null, cb); +} + +void LLAppearanceMgr::onOutfitFolderCreatedAndClothingOrdered(const LLUUID& folder_id, bool show_panel) +{ + LLPointer cb = + new LLBoostFuncInventoryCallback(no_op_inventory_func, + boost::bind(show_created_outfit,folder_id,show_panel)); + bool copy_folder_links = false; + slamCategoryLinks(getCOF(), folder_id, copy_folder_links, cb); +} + LLUUID LLAppearanceMgr::makeNewOutfitCore(const std::string& new_folder_name, bool show_panel, LLInventoryModel::item_array_t* items /*=NULL*/) { if (!isAgentAvatarValid()) return LLUUID::null; + LL_DEBUGS("Avatar") << "creating new outfit" << LL_ENDL; + gAgentWearables.notifyLoadingStarted(); // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - LLUUID folder_id = gInventory.createNewCategory( - parent_id, - LLFolderType::FT_OUTFIT, - new_folder_name); - - updateClothingOrderingInfo(); - - inventory_func_type fire_fn = no_op_inventory_func; - if(show_panel) - fire_fn = boost::bind(&scroll_to_folder,folder_id); - - LLPointer cb = new LLBoostFuncInventoryCallbackFireOnce( fire_fn, - boost::bind(&show_created_outfit,folder_id,show_panel)); - - if(items) - copyItems(folder_id,items,cb); - else - shallowCopyCategoryContents(getCOF(),folder_id,cb); - - createBaseOutfitLink(folder_id, cb); - - dumpCat(folder_id,"COF, new outfit"); - - return folder_id; + { + LLUUID folder_id = gInventory.createNewCategory( + parent_id, + LLFolderType::FT_OUTFIT, + new_folder_name); + onOutfitFolderCreated(folder_id, show_panel); + return folder_id; + } } LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel/*=true*/) { @@ -3844,13 +4228,7 @@ private: {} virtual void dispatch() { - link_inventory_item(gAgent.getID(), - mItem->getLinkedUUID(), - mFolderID, - mItem->getName(), - mDesc, - LLAssetType::AT_LINK, - this); + link_inventory_object(mFolderID,mItem.get(),this); } private: const std::string mDesc; @@ -4023,51 +4401,68 @@ void LLAppearanceMgr::wearBaseOutfit() void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove) { if (ids_to_remove.empty()) - { - LL_WARNS() << "called with empty list, nothing to do" << LL_ENDL; - } - -// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) - bool fUpdateAppearance = false; + { + LL_WARNS() << "called with empty list, nothing to do" << LL_ENDL; + return; + } + LLPointer cb = new LLUpdateAppearanceOnDestroy; for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it) { - const LLUUID& linked_item_id = gInventory.getLinkedItemID(*it); + const LLUUID& id_to_remove = *it; + const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove); +// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(gInventory.getItem(linked_item_id))) ) { continue; } - - fUpdateAppearance = true; - removeCOFItemLinks(linked_item_id); - } - - if (fUpdateAppearance) - { - updateAppearanceFromCOF(); - } // [/RLVa:KB] -// for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it) -// { -// const LLUUID& id_to_remove = *it; -// const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove); -// removeCOFItemLinks(linked_item_id); -// } + removeCOFItemLinks(linked_item_id, cb); + addDoomedTempAttachment(linked_item_id); + } // updateAppearanceFromCOF(); } void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) { -// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove); +// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(gInventory.getItem(linked_item_id))) ) { return; } // [/RLVA:KB] - removeCOFItemLinks(linked_item_id); - updateAppearanceFromCOF(); + LLPointer cb = new LLUpdateAppearanceOnDestroy; + removeCOFItemLinks(linked_item_id, cb); + addDoomedTempAttachment(linked_item_id); } + +// Adds the given item ID to mDoomedTempAttachmentIDs iff it's a temp attachment +void LLAppearanceMgr::addDoomedTempAttachment(const LLUUID& id_to_remove) +{ + LLViewerObject * attachmentp = gAgentAvatarp->findAttachmentByID(id_to_remove); + if (attachmentp && + attachmentp->isTempAttachment()) + { // If this is a temp attachment and we want to remove it, record the ID + // so it will be deleted when attachments are synced up with COF + mDoomedTempAttachmentIDs.insert(id_to_remove); + //LL_INFOS() << "Will remove temp attachment id " << id_to_remove << LL_ENDL; + } +} + +// Find AND REMOVES the given UUID from mDoomedTempAttachmentIDs +bool LLAppearanceMgr::shouldRemoveTempAttachment(const LLUUID& item_id) +{ + doomed_temp_attachments_t::iterator iter = mDoomedTempAttachmentIDs.find(item_id); + if (iter != mDoomedTempAttachmentIDs.end()) + { + mDoomedTempAttachmentIDs.erase(iter); + return true; + } + return false; +} + + bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) { if (!item || !item->isWearableType()) return false; @@ -4098,7 +4493,12 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b swap_item->setDescription(item->getActualDescription()); item->setDescription(tmp); + // LL_DEBUGS("Inventory") << "swap, item " + // << ll_pretty_print_sd(item->asLLSD()) + // << " swap_item " + // << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL; + // FIXME switch to use AISv3 where supported. //items need to be updated on a dataserver item->setComplete(TRUE); item->updateServer(FALSE); @@ -4155,7 +4555,7 @@ void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg) } void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items, - const std::string& msg) + const std::string& msg) { for (S32 i=0; igetAssetUUID(); } - LL_DEBUGS("Avatar") << self_av_string() << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << LL_ENDL; + LL_DEBUGS("Avatar") << self_av_string() << msg << " " << i << " " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << LL_ENDL; } } @@ -4175,8 +4575,7 @@ LLAppearanceMgr::LLAppearanceMgr(): mOutfitIsDirty(false), mOutfitLocked(false), mIsInUpdateAppearanceFromCOF(false), - mLastUpdateRequestCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), - mLastAppearanceUpdateCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN) + mAppearanceResponder(new RequestAgentUpdateAppearanceResponder) { LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); @@ -4196,14 +4595,8 @@ LLAppearanceMgr::~LLAppearanceMgr() void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val) { - LL_INFOS() << "setAttachmentInvLinkEnable => " << (int) val << LL_ENDL; + LL_DEBUGS("Avatar") << "setAttachmentInvLinkEnable => " << (int)val << LL_ENDL; mAttachmentInvLinkEnabled = val; -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-10-05 (Catznip-3.0.0a) | Added: Catznip-2.2.0a - if (mAttachmentInvLinkEnabled) - { - linkPendingAttachments(); - } -// [/SL:KB] } void dumpAttachmentSet(const std::set& atts, const std::string& msg) @@ -4226,24 +4619,17 @@ void dumpAttachmentSet(const std::set& atts, const std::string& msg) void LLAppearanceMgr::registerAttachment(const LLUUID& item_id) { gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-10-05 (Catznip-3.0.0a) | Added: Catznip-2.2.0a - if (isLinkInCOF(item_id)) - { - return; - } - mPendingAttachLinks.push_back(item_id); -// [/SL:KB] if (mAttachmentInvLinkEnabled) { // we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF. // it will trigger gAgentWariables.notifyLoadingFinished() // But it is not acceptable solution. See EXT-7777 -// LLAppearanceMgr::addCOFItemLink(item_id, false); // Add COF link for item. -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-10-05 (Catznip-3.0.0a) | Modified: Catznip-2.2.0a - LLPointer cb = new LLRegisterAttachmentCallback(); - LLAppearanceMgr::addCOFItemLink(item_id, false, cb); // Add COF link for item. -// [/SL:KB] + if (!isLinkedInCOF(item_id)) + { + LLPointer cb = new LLUpdateAppearanceOnDestroy(); + LLAppearanceMgr::addCOFItemLink(item_id, cb); // Add COF link for item. + } } else { @@ -4254,13 +4640,6 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id) void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id) { gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-10-05 (Catznip-3.0.0a) | Added: Catznip-2.2.0a - uuid_vec_t::iterator itPendingAttachLink = std::find(mPendingAttachLinks.begin(), mPendingAttachLinks.end(), item_id); - if (itPendingAttachLink != mPendingAttachLinks.end()) - { - mPendingAttachLinks.erase(itPendingAttachLink); - } -// [/SL:KB] if (mAttachmentInvLinkEnabled) { @@ -4272,56 +4651,23 @@ void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id) } } -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-18 (Catznip-3.0.0a) | Modified: Catznip-2.2.0a -void LLAppearanceMgr::linkPendingAttachments() -{ - LLPointer cb = NULL; - for (uuid_vec_t::const_iterator itPendingAttachLink = mPendingAttachLinks.begin(); - itPendingAttachLink != mPendingAttachLinks.end(); ++itPendingAttachLink) - { - const LLUUID& idAttachItem = *itPendingAttachLink; - if ( (gAgentAvatarp->isWearingAttachment(idAttachItem)) && (!isLinkInCOF(idAttachItem)) ) - { - if (!cb) - cb = new LLRegisterAttachmentCallback(); - LLAppearanceMgr::addCOFItemLink(idAttachItem, false, cb); - } - } -} - -void LLAppearanceMgr::onRegisterAttachmentComplete(const LLUUID& idItem) -{ - const LLUUID& idItemBase = gInventory.getLinkedItemID(idItem); - - // Remove the attachment from the pending list - uuid_vec_t::iterator itPendingAttachLink = std::find(mPendingAttachLinks.begin(), mPendingAttachLinks.end(), idItemBase); - if (itPendingAttachLink != mPendingAttachLinks.end()) - mPendingAttachLinks.erase(itPendingAttachLink); - - // It may have been detached already in which case we should remove the COF link - if ( (isAgentAvatarValid()) && (!gAgentAvatarp->isWearingAttachment(idItemBase)) ) - removeCOFItemLinks(idItemBase); -} -// [/SL:KB] - BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const { - return gInventory.isObjectDescendentOf(obj_id, getCOF()); + const LLUUID& cof = getCOF(); + if (obj_id == cof) + return TRUE; + const LLInventoryObject* obj = gInventory.getObject(obj_id); + if (obj && obj->getParentUUID() == cof) + return TRUE; + return FALSE; } // static bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id) { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLLinkedItemIDMatches find_links(gInventory.getLinkedItemID(obj_id)); - gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - find_links); - - return !items.empty(); + const LLUUID& target_id = gInventory.getLinkedItemID(obj_id); + LLLinkedItemIDMatches find_links(target_id); + return gInventory.hasMatchingDirectDescendent(LLAppearanceMgr::instance().getCOF(), find_links); } BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const @@ -4338,18 +4684,6 @@ BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const // For now, don't allow direct deletion from the COF. Instead, force users // to choose "Detach" or "Take Off". return TRUE; - /* - const LLInventoryObject *obj = gInventory.getObject(obj_id); - if (!obj) return FALSE; - - // Can't delete bodyparts, since this would be equivalent to removing the item. - if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE; - - // Can't delete the folder link, since this is saved for bookkeeping. - if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE; - - return FALSE; - */ } class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver @@ -4390,9 +4724,9 @@ public: } virtual void done() { - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. + // 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(), @@ -4404,12 +4738,9 @@ public: { LL_WARNS() << "Nothing fetched in category " << mComplete.front() << LL_ENDL; - //dec_busy_count(); gInventory.removeObserver(this); doOnIdleOneTime(mCallable); - // lets notify observers that loading is finished. - //gAgentWearables.notifyLoadingFinished(); delete this; return; } @@ -4460,16 +4791,7 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb) void wear_multiple(const uuid_vec_t& ids, bool replace) { LLPointer cb = new LLUpdateAppearanceOnDestroy; - - bool first = true; - uuid_vec_t::const_iterator it; - for (it = ids.begin(); it != ids.end(); ++it) - { - // if replace is requested, the first item worn will replace the current top - // item, and others will be added. - LLAppearanceMgr::instance().wearItemOnAvatar(*it,false,first && replace,cb); - first = false; - } + LLAppearanceMgr::instance().wearItemsOnAvatar(ids, false, replace, cb); } // SLapp for easy-wearing of a stock (library) avatar @@ -4483,19 +4805,33 @@ public: bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { - LLPointer category = new LLInventoryCategory(query_map["folder_id"], - LLUUID::null, - LLFolderType::FT_CLOTHING, - "Quick Appearance"); - LLSD::UUID folder_uuid = query_map["folder_id"].asUUID(); - if ( gInventory.getCategory( folder_uuid ) != NULL && - folder_uuid != gInventory.getRootFolderID() && - folder_uuid != gInventory.getLibraryRootFolderID() ) - { - LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); + LLSD::UUID folder_uuid; - // *TODOw: This may not be necessary if initial outfit is chosen already -- josh - gAgent.setGenderChosen(TRUE); + if (folder_uuid.isNull() && query_map.has("folder_name")) + { + std::string outfit_folder_name = query_map["folder_name"]; + folder_uuid = findDescendentCategoryIDByName( + gInventory.getLibraryRootFolderID(), + outfit_folder_name); + } + if (folder_uuid.isNull() && query_map.has("folder_id")) + { + folder_uuid = query_map["folder_id"].asUUID(); + } + + if (folder_uuid.notNull()) + { + LLPointer category = new LLInventoryCategory(folder_uuid, + LLUUID::null, + LLFolderType::FT_CLOTHING, + "Quick Appearance"); + if ( gInventory.getCategory( folder_uuid ) != NULL ) + { + LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); + + // *TODOw: This may not be necessary if initial outfit is chosen already -- josh + gAgent.setGenderChosen(TRUE); + } } // release avatar picker keyboard focus diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 887e7991a..b268db601 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -40,7 +40,7 @@ class LLWearable; class LLWearableHoldingPattern; class LLInventoryCallback; class LLOutfitUnLockTimer; -class LLCallAfterInventoryLinkMgr; +class RequestAgentUpdateAppearanceResponder; class LLAppearanceMgr: public LLSingleton { @@ -52,9 +52,11 @@ class LLAppearanceMgr: public LLSingleton public: typedef std::vector wearables_by_type_t; - void updateAppearanceFromCOF(bool update_base_outfit_ordering = false); + void updateAppearanceFromCOF(bool enforce_item_restrictions = true, + bool enforce_ordering = true, + nullary_func_t post_update_func = no_op); // [SL:KB] - Patch: Appearance-MixedViewers | Checked: 2010-04-02 (Catznip-3.0.0a) | Added: Catznip-2.0.0a - void updateAppearanceFromInitialWearables(LLInventoryModel::item_array_t& initial_items); + void updateAppearanceFromInitialWearables(LLInventoryObject::const_object_list_t& initial_items); // [/SL:KB] bool needToSaveCOF(); void updateCOF(const LLUUID& category, bool append = false); @@ -74,18 +76,24 @@ public: void addCategoryToCurrentOutfit(const LLUUID& cat_id); S32 findExcessOrDuplicateItems(const LLUUID& cat_id, LLAssetType::EType type, - S32 max_items, - LLInventoryModel::item_array_t& items_to_kill); - void enforceItemRestrictions(); + S32 max_items_per_type, + S32 max_items_total, + LLInventoryObject::object_list_t& items_to_kill); + void findAllExcessOrDuplicateItems(const LLUUID& cat_id, + LLInventoryObject::object_list_t& items_to_kill); + void enforceCOFItemRestrictions(LLPointer cb); S32 getActiveCopyOperations() const; + // Replace category contents with copied links via the slam_inventory_folder + // command (single inventory operation where supported) + void slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id, + bool include_folder_links, LLPointer cb); + // Copy all items and the src category itself. void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, LLPointer cb); - void copyItems(const LLUUID& dst_id, LLInventoryModel::item_array_t* items, LLPointer cb); - // Return whether this folder contains minimal contents suitable for making a full outfit. BOOL getCanMakeFolderIntoOutfit(const LLUUID& folder_id); @@ -101,21 +109,20 @@ public: // Determine whether we can replace current outfit with the given one. bool getCanReplaceCOF(const LLUUID& outfit_cat_id); + // Can we add all referenced items to the avatar? + bool canAddWearables(const uuid_vec_t& item_ids); + // Copy all items in a category. void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, LLPointer cb); + void copyItems(const LLUUID& dst_id, LLInventoryModel::item_array_t* items, LLPointer cb); // Find the Current Outfit folder. const LLUUID getCOF() const; S32 getCOFVersion() const; - S32 mLastUpdateRequestCOFVersion; - S32 getLastUpdateRequestCOFVersion() const; - - // COF version of last appearance message received for self av. - S32 mLastAppearanceUpdateCOFVersion; - S32 getLastAppearanceUpdateCOFVersion() const; - void setLastAppearanceUpdateCOFVersion(S32 new_val); + // Debugging - get truncated LLSD summary of COF contents. + LLSD dumpCOF() const; // Finds the folder link to the currently worn outfit const LLViewerInventoryItem *getBaseOutfitLink(); @@ -124,17 +131,24 @@ public: // find the UUID of the currently worn outfit (Base Outfit) const LLUUID getBaseOutfitUUID(); + void wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, + bool do_update, + bool replace, + LLPointer cb = NULL); + // Wear/attach an item (from a user's inventory) on the agent - bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false, LLPointer cb = NULL); + void wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false, + LLPointer cb = NULL); // Update the displayed outfit name in UI. void updatePanelOutfitName(const std::string& name); - void purgeBaseOutfitLink(const LLUUID& category); + void purgeBaseOutfitLink(const LLUUID& category, LLPointer cb = NULL); void createBaseOutfitLink(const LLUUID& category, LLPointer link_waiter); - void updateAgentWearables(LLWearableHoldingPattern* holder, bool append); + void updateAgentWearables(LLWearableHoldingPattern* holder); + S32 countActiveHoldingPatterns(); // For debugging - could be moved elsewhere. void dumpCat(const LLUUID& cat_id, const std::string& msg); void dumpItemArray(const LLInventoryModel::item_array_t& items, const std::string& msg); @@ -144,25 +158,23 @@ public: void registerAttachment(const LLUUID& item_id); void setAttachmentInvLinkEnable(bool val); - // utility function for bulk linking. - void linkAll(const LLUUID& category, - LLInventoryModel::item_array_t& items, - LLPointer cb); - // Add COF link to individual item. - void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer cb = NULL, const std::string description = ""); - void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer cb = NULL, const std::string description = ""); + void addCOFItemLink(const LLUUID& item_id, LLPointer cb = NULL, const std::string description = ""); + void addCOFItemLink(const LLInventoryItem *item, LLPointer cb = NULL, const std::string description = ""); // Find COF entries referencing the given item. LLInventoryModel::item_array_t findCOFItemLinks(const LLUUID& item_id); bool isLinkedInCOF(const LLUUID& item_id); - + // Remove COF entries - void removeCOFItemLinks(const LLUUID& item_id); - void removeCOFLinksOfType(LLWearableType::EType type); + void removeCOFItemLinks(const LLUUID& item_id, LLPointer cb = NULL); + void removeCOFLinksOfType(LLWearableType::EType type, LLPointer cb = NULL); void removeAllClothesFromAvatar(); void removeAllAttachmentsFromAvatar(); + // Special handling of temp attachments, which are not in the COF + bool shouldRemoveTempAttachment(const LLUUID& item_id); + //has the current outfit changed since it was loaded? bool isOutfitDirty() { return mOutfitIsDirty; } @@ -173,12 +185,11 @@ public: // should only be necessary to do on initial login. void updateIsDirty(); + void setOutfitLocked(bool locked); + // Called when self avatar is first fully visible. void onFirstFullyVisible(); - // Create initial outfits from library. - void autopopulateOutfits(); - // Copy initial gestures from library. void copyLibraryGestures(); @@ -192,6 +203,10 @@ public: void removeItemsFromAvatar(const uuid_vec_t& item_ids); void removeItemFromAvatar(const LLUUID& item_id); + + void onOutfitFolderCreated(const LLUUID& folder_id, bool show_panel); + void onOutfitFolderCreatedAndClothingOrdered(const LLUUID& folder_id, bool show_panel); + private: LLUUID makeNewOutfitCore(const std::string& new_folder_name, bool show_panel/*=true*/, LLInventoryModel::item_array_t* items = NULL); public: @@ -206,15 +221,29 @@ public: //Divvy items into arrays by wearable type static void divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type); + typedef std::map desc_map_t; + + void getWearableOrderingDescUpdates(LLInventoryModel::item_array_t& wear_items, desc_map_t& desc_map); + //Check ordering information on wearables stored in links' descriptions and update if it is invalid // COF is processed if cat_id is not specified - void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null, bool update_base_outfit_ordering = false); + bool validateClothingOrderingInfo(LLUUID cat_id = LLUUID::null); + + void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null, + LLPointer cb = NULL); bool isOutfitLocked() { return mOutfitLocked; } bool isInUpdateAppearanceFromCOF() { return mIsInUpdateAppearanceFromCOF; } - void requestServerAppearanceUpdate(LLHTTPClient::ResponderPtr responder_ptr = NULL); + void requestServerAppearanceUpdate(); + + void incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr = NULL); + + U32 getNumAttachmentsInCOF(); + + // *HACK Remove this after server side texture baking is deployed on all sims. + void incrementCofVersionLegacy(); void setAppearanceServiceURL(const std::string& url) { mAppearanceServiceURL = url; } std::string getAppearanceServiceURL() const; @@ -229,51 +258,47 @@ protected: private: - void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type); + void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total); void getDescendentsOfAssetType(const LLUUID& category, LLInventoryModel::item_array_t& items, LLAssetType::EType type, - bool follow_folder_links); + bool follow_folder_links = false); void getUserDescendents(const LLUUID& category, LLInventoryModel::item_array_t& wear_items, LLInventoryModel::item_array_t& obj_items, LLInventoryModel::item_array_t& gest_items, - bool follow_folder_links); + bool follow_folder_links = false); void purgeCategory(const LLUUID& category, bool keep_outfit_links); static void onOutfitRename(const LLSD& notification, const LLSD& response); - void setOutfitLocked(bool locked); - // [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f void purgeItems(const LLInventoryModel::item_array_t& items); void purgeItemsOfType(LLAssetType::EType asset_type); - void syncCOF(const LLInventoryModel::item_array_t& items, - LLInventoryModel::item_array_t& items_to_add, LLInventoryModel::item_array_t& items_to_remove); // [/SL:KB] bool mAttachmentInvLinkEnabled; bool mOutfitIsDirty; bool mIsInUpdateAppearanceFromCOF; // to detect recursive calls. + boost::intrusive_ptr mAppearanceResponder; + /** * Lock for blocking operations on outfit until server reply or timeout exceed * to avoid unsynchronized outfit state or performing duplicate operations. */ bool mOutfitLocked; - boost::scoped_ptr mUnlockOutfitTimer; + std::auto_ptr mUnlockOutfitTimer; -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-09-18 (Catznip-3.0.0a) | Modified: Catznip-2.1.2e -public: - void linkPendingAttachments(); - void onRegisterAttachmentComplete(const LLUUID& idItem); -private: - uuid_vec_t mPendingAttachLinks; -// [/SL:KB] + // Set of temp attachment UUIDs that should be removed + typedef std::set doomed_temp_attachments_t; + doomed_temp_attachments_t mDoomedTempAttachmentIDs; + + void addDoomedTempAttachment(const LLUUID& id_to_remove); ////////////////////////////////////////////////////////////////////////////////// // Item-specific convenience functions @@ -292,27 +317,31 @@ public: class LLUpdateAppearanceOnDestroy: public LLInventoryCallback { public: - LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering = false); + LLUpdateAppearanceOnDestroy(bool enforce_item_restrictions = true, + bool enforce_ordering = true, + nullary_func_t post_update_func = no_op); virtual ~LLUpdateAppearanceOnDestroy(); /* virtual */ void fire(const LLUUID& inv_item); private: U32 mFireCount; - bool mUpdateBaseOrder; + bool mEnforceItemRestrictions; + bool mEnforceOrdering; + nullary_func_t mPostUpdateFunc; }; -// [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-08-31 (Catznip-3.0.0a) | Added: Catznip-2.1.2a -class LLRegisterAttachmentCallback : public LLInventoryCallback +class LLUpdateAppearanceAndEditWearableOnDestroy: public LLInventoryCallback { public: - /*virtual*/ void fire(const LLUUID& idItem) - { - LLAppearanceMgr::instance().onRegisterAttachmentComplete(idItem); - } -}; -// [/SL:KB] + LLUpdateAppearanceAndEditWearableOnDestroy(const LLUUID& item_id); -#define SUPPORT_ENSEMBLES 0 + /* virtual */ void fire(const LLUUID& item_id) {} + + ~LLUpdateAppearanceAndEditWearableOnDestroy(); + +private: + LLUUID mItemID; +}; LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 97ae0f707..42eb40db5 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1623,6 +1623,8 @@ bool LLAppViewer::cleanup() LLAvatarAppearance::cleanupClass(); + LLAvatarAppearance::cleanupClass(); + LLPostProcess::cleanupClass(); LLTracker::cleanupInstance(); diff --git a/indra/newview/llassetuploadqueue.cpp b/indra/newview/llassetuploadqueue.cpp index fcfa31dd0..88695b2d1 100644 --- a/indra/newview/llassetuploadqueue.cpp +++ b/indra/newview/llassetuploadqueue.cpp @@ -47,7 +47,7 @@ public: LLAssetUploadChainResponder(const LLSD& post_data, const std::string& file_name, const LLUUID& queue_id, - char* data, + U8* data, U32 data_size, std::string script_name, LLAssetUploadQueueSupplier *supplier) : @@ -129,7 +129,9 @@ public: for(LLSD::array_const_iterator line = compile_errors.beginArray(); line < compile_errors.endArray(); line++) { - mSupplier->log(line->asString()); + std::string str = line->asString(); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + mSupplier->log(str); LL_INFOS() << content["errors"] << LL_ENDL; } } @@ -139,7 +141,7 @@ public: /*virtual*/ char const* getName(void) const { return "LLAssetUploadChainResponder"; } LLAssetUploadQueueSupplier *mSupplier; - char* mData; + U8* mData; U32 mDataSize; std::string mScriptName; }; @@ -190,7 +192,7 @@ void LLAssetUploadQueue::queue(const std::string& filename, BOOL is_running, BOOL is_target_mono, const LLUUID& queue_id, - char* script_data, + U8* script_data, U32 data_size, std::string script_name) { diff --git a/indra/newview/llassetuploadqueue.h b/indra/newview/llassetuploadqueue.h index eaee9f424..930dc72a7 100644 --- a/indra/newview/llassetuploadqueue.h +++ b/indra/newview/llassetuploadqueue.h @@ -54,7 +54,7 @@ public: BOOL is_running, BOOL is_target_mono, const LLUUID& queue_id, - char* data, + U8* data, U32 data_size, std::string script_name); @@ -72,7 +72,7 @@ private: BOOL mIsRunning; BOOL mIsTargetMono; LLUUID mQueueId; - char* mData; + U8* mData; U32 mDataSize; std::string mScriptName; }; diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index af15459da..8d3dd6243 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -416,8 +416,8 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id, { // Read script source in to buffer. U32 script_size = file.getSize(); - char* script_data = new char[script_size]; - file.read((U8*)script_data, script_size); + U8* script_data = new U8[script_size]; + file.read(script_data, script_size); queue->mUploadQueue->queue(filename, data->mTaskId, data->mItemId, is_running, queue->mMono, queue->getID(), diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 9f43828c5..c03c8f1a5 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -753,15 +753,11 @@ void LLFloaterCustomize::saveCurrentWearables() if (link_item) { // Create new link - link_inventory_item( gAgent.getID(), - link_item->getLinkedUUID(), + link_inventory_object( LLAppearanceMgr::instance().getCOF(), - link_item->getName(), - description, - LLAssetType::AT_LINK, - NULL); + link_item, NULL); // Remove old link - gInventory.purgeObject(link_item->getUUID()); + remove_inventory_object(link_item->getUUID(), NULL); } } gAgentWearables.saveWearable( cur, i ); diff --git a/indra/newview/llfloaterlandmark.cpp b/indra/newview/llfloaterlandmark.cpp index cf5ebef59..7a8ac4534 100644 --- a/indra/newview/llfloaterlandmark.cpp +++ b/indra/newview/llfloaterlandmark.cpp @@ -352,15 +352,6 @@ void LLFloaterLandmark::onBtnDelete() gInventory.notifyObservers(); } } - - // Delete the item entirely - /* - item->removeFromServer(); - gInventory.deleteObject(item->getUUID()); - gInventory.notifyObservers(); - */ - - } void LLFloaterLandmark::onBtnRename() diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index 6cea68f66..14f5965f6 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -129,7 +129,7 @@ void LLFloaterOpenObject::dirty() -void LLFloaterOpenObject::moveToInventory(bool wear) +void LLFloaterOpenObject::moveToInventory(bool wear, bool replace) { if (mObjectSelection->getRootObjectCount() != 1) { @@ -157,40 +157,34 @@ void LLFloaterOpenObject::moveToInventory(bool wear) parent_category_id = gInventory.getRootFolderID(); } - LLCategoryCreate* cat_data = new LLCategoryCreate(object_id, wear); - + 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, - callbackCreateInventoryCategory, - (void*)cat_data); + 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() ) { - LLSD result; - result["folder_id"] = category_id; //Reduce redundant code by just calling the callback. Dur. - callbackCreateInventoryCategory(result,cat_data); + callbackCreateInventoryCategory(category_id, object_id, wear); } } // static -void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLSD& result, void* data) +void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLUUID& category_id, LLUUID object_id, bool wear, bool replace) { - LLCategoryCreate* cat_data = (LLCategoryCreate*)data; - - LLUUID category_id = result["folder_id"].asUUID(); LLCatAndWear* wear_data = new LLCatAndWear; wear_data->mCatID = category_id; - wear_data->mWear = cat_data->mWear; + wear_data->mWear = wear; wear_data->mFolderResponded = true; - + wear_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(cat_data->mObjectID, category_id, TRUE, + BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE, callbackMoveInventory, (void*)wear_data); if (!success) @@ -200,7 +194,6 @@ void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLSD& result, vo LLNotificationsUtil::add("OpenObjectCannotCopy"); } - delete cat_data; } // static diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h index 7639fd9f8..1f5137c4d 100644 --- a/indra/newview/llfloateropenobject.h +++ b/indra/newview/llfloateropenobject.h @@ -64,6 +64,7 @@ public: LLUUID mCatID; bool mWear; bool mFolderResponded; + bool mReplace; }; protected: @@ -73,11 +74,11 @@ protected: void refresh(); void draw(); - void moveToInventory(bool wear); + void moveToInventory(bool wear, bool replace = false); static void onClickMoveToInventory(void* data); static void onClickMoveAndWear(void* data); - static void callbackCreateInventoryCategory(const LLSD& result, void* data); + static void callbackCreateInventoryCategory(const LLUUID& category_id, LLUUID object_id, bool wear, bool replace = false); static void callbackMoveInventory(S32 result, void* data); static void* createPanelInventory(void* data); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index d29fa91b4..8826e0421 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -176,7 +176,7 @@ void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) // Default constructor LLFolderView::LLFolderView( const std::string& name, - const LLRect& rect, const LLUUID& source_id, LLPanel* parent_panel, LLFolderViewEventListener* listener ) : + const LLRect& rect, const LLUUID& source_id, LLPanel* parent_panel, LLFolderViewEventListener* listener, LLFolderViewGroupedItemModel* group_model ) : #if LL_WINDOWS #pragma warning( push ) #pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list @@ -211,7 +211,8 @@ LLFolderView::LLFolderView( const std::string& name, mUseEllipses(FALSE), mDraggingOverItem(NULL), mStatusTextBox(NULL), - mSearchType(1) + mSearchType(1), + mGroupedItemModel(group_model) { LLPanel* panel = parent_panel; mParentPanel = panel->getHandle(); @@ -2362,6 +2363,14 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu) flags = 0x0; } + // 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. + if (getFolderViewGroupedItemModel()) + { + getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems, *menu); + } + addNoOptions(menu); } diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 76f32e1a3..5311fb5f4 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -53,6 +53,7 @@ #include "llviewertexture.h" class LLFolderViewEventListener; +class LLFolderViewGroupedItemModel; class LLFolderViewFolder; class LLFolderViewItem; class LLInventoryModel; @@ -77,13 +78,19 @@ public: LLFolderView( const std::string& name, const LLRect& rect, - const LLUUID& source_id, LLPanel *parent_view, LLFolderViewEventListener* listener ); + const LLUUID& source_id, LLPanel *parent_view, LLFolderViewEventListener* listener, LLFolderViewGroupedItemModel* group_model = NULL ); + + typedef folder_view_item_deque selected_items_t; + virtual ~LLFolderView( void ); virtual BOOL canFocusChildren() const; virtual LLFolderView* getRoot() { return this; } + LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } + const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; } + // FolderViews default to sort by name. This will change that, // and resort the items if necessary. void setSortOrder(U32 order); @@ -321,6 +328,8 @@ protected: LLHandle mParentPanel; + LLFolderViewGroupedItemModel* mGroupedItemModel; + /** * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index a8ad4ca0e..56990360a 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -355,7 +355,7 @@ protected: LLUIImagePtr icon_open, LLUIImagePtr icon_link, LLFolderView* root, - LLFolderViewEventListener* listener ); + LLFolderViewEventListener* listener); friend class LLBuildNewViewsScheduler; friend class LLPanelObjectInventory; @@ -572,4 +572,12 @@ public: virtual void operator()(LLFolderViewEventListener* listener) = 0; }; +typedef std::deque folder_view_item_deque; + +class LLFolderViewGroupedItemModel : public LLRefCount +{ +public: + virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0; +}; + #endif // LLFOLDERVIEWITEM_H diff --git a/indra/newview/llhttpretrypolicy.cpp b/indra/newview/llhttpretrypolicy.cpp new file mode 100644 index 000000000..1fb287cd6 --- /dev/null +++ b/indra/newview/llhttpretrypolicy.cpp @@ -0,0 +1,155 @@ +/** + * @file llhttpretrypolicy.h + * @brief Header for a retry policy class intended for use with http responders. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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 "llhttpretrypolicy.h" +#include "llhttpstatuscodes.h" +#include "aihttpheaders.h" + +// for curl_getdate() (apparently parsing RFC 1123 dates is hard) +#include + +// Parses 'Retry-After' header contents and returns seconds until retry should occur. +bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait) +{ + // *TODO: This needs testing! Not in use yet. + // Examples of Retry-After headers: + // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT + // Retry-After: 120 + + // Check for number of seconds version, first: + char* end = 0; + // Parse as double + double seconds = std::strtod(retry_after.c_str(), &end); + if (end != 0 && *end == 0) + { + // Successful parse + seconds_to_wait = (F32)seconds; + return true; + } + + // Parse rfc1123 date. + time_t date = curl_getdate(retry_after.c_str(), NULL); + if (-1 == date) return false; + + seconds_to_wait = (F64)date - LLTimer::getTotalSeconds(); + + return true; +} + +LLAdaptiveRetryPolicy::LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries, bool retry_on_4xx): + mMinDelay(min_delay), + mMaxDelay(max_delay), + mBackoffFactor(backoff_factor), + mMaxRetries(max_retries), + mRetryOn4xx(retry_on_4xx) +{ + init(); +} + +void LLAdaptiveRetryPolicy::init() +{ + mDelay = mMinDelay; + mRetryCount = 0; + mShouldRetry = true; +} + +void LLAdaptiveRetryPolicy::reset() +{ + init(); +} + +bool LLAdaptiveRetryPolicy::getRetryAfter(const AIHTTPReceivedHeaders& headers, F32& retry_header_time) +{ + AIHTTPReceivedHeaders::range_type results; + return headers.getValues("retry-after", results) && getSecondsUntilRetryAfter(results.first->second, retry_header_time); +} + +void LLAdaptiveRetryPolicy::onSuccess() +{ + init(); +} + +void LLAdaptiveRetryPolicy::onFailure(S32 status, const AIHTTPReceivedHeaders& headers) +{ + F32 retry_header_time; + bool has_retry_header_time = getRetryAfter(headers,retry_header_time); + onFailureCommon(status, has_retry_header_time, retry_header_time); +} + + +void LLAdaptiveRetryPolicy::onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time) +{ + if (!mShouldRetry) + { + LL_INFOS() << "keep on failing" << LL_ENDL; + return; + } + if (mRetryCount > 0) + { + mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay); + } + // Honor server Retry-After header. + // Status 503 may ask us to wait for a certain amount of time before retrying. + F32 wait_time = mDelay; + if (has_retry_header_time) + { + wait_time = retry_header_time; + } + + if (mRetryCount>=mMaxRetries) + { + LL_INFOS() << "Too many retries " << mRetryCount << ", will not retry" << LL_ENDL; + mShouldRetry = false; + } + + if (!mRetryOn4xx && !(status == HTTP_INTERNAL_ERROR_OTHER || ((500 <= status) && (status < 600)))) + { + LL_INFOS() << "Non-server error " << status << ", will not retry" << LL_ENDL; + mShouldRetry = false; + } + if (mShouldRetry) + { + LL_INFOS() << "Retry count " << mRetryCount << " should retry after " << wait_time << LL_ENDL; + mRetryTimer.reset(); + mRetryTimer.setTimerExpirySec(wait_time); + } + mRetryCount++; +} + + +bool LLAdaptiveRetryPolicy::shouldRetry(F32& seconds_to_wait) const +{ + if (mRetryCount == 0) + { + // Called shouldRetry before any failure. + seconds_to_wait = F32_MAX; + return false; + } + seconds_to_wait = mShouldRetry ? (F32) mRetryTimer.getRemainingTimeF32() : F32_MAX; + return mShouldRetry; +} diff --git a/indra/newview/llhttpretrypolicy.h b/indra/newview/llhttpretrypolicy.h new file mode 100644 index 000000000..58f91f300 --- /dev/null +++ b/indra/newview/llhttpretrypolicy.h @@ -0,0 +1,88 @@ +/** + * @file file llhttpretrypolicy.h + * @brief declarations for http retry policy class. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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_RETRYPOLICY_H +#define LL_RETRYPOLICY_H + +#include "lltimer.h" +#include "llthread.h" + +class AIHTTPReceivedHeaders; + +// This is intended for use with HTTP Clients/Responders, but is not +// specifically coupled with those classes. +class LLHTTPRetryPolicy: public LLThreadSafeRefCount +{ +public: + LLHTTPRetryPolicy() {} + + virtual ~LLHTTPRetryPolicy() {} + // Call after a sucess to reset retry state. + + virtual void onSuccess() = 0; + // Call once after an HTTP failure to update state. + virtual void onFailure(S32 status, const AIHTTPReceivedHeaders& headers) = 0; + + virtual bool shouldRetry(F32& seconds_to_wait) const = 0; + + virtual void reset() = 0; +}; + +// Very general policy with geometric back-off after failures, +// up to a maximum delay, and maximum number of retries. +class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy +{ +public: + LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries, bool retry_on_4xx = false); + + // virtual + void onSuccess(); + + void reset(); + + // virtual + void onFailure(S32 status, const AIHTTPReceivedHeaders& headers); + // virtual + bool shouldRetry(F32& seconds_to_wait) const; + +protected: + void init(); + bool getRetryAfter(const AIHTTPReceivedHeaders& headers, F32& retry_header_time); + void onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time); + +private: + F32 mMinDelay; // delay never less than this value + F32 mMaxDelay; // delay never exceeds this value + F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay. + U32 mMaxRetries; // maximum number of times shouldRetry will return true. + F32 mDelay; // current delay. + U32 mRetryCount; // number of times shouldRetry has been called. + LLTimer mRetryTimer; // time until next retry. + bool mShouldRetry; // Becomes false after too many retries, or the wrong sort of status received, etc. + bool mRetryOn4xx; // Normally only retry on 5xx server errors. +}; + +#endif diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index 1ae08de33..02429f13f 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -71,6 +71,8 @@ typedef LLMemberListener inventory_panel_listener_t; bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) { + if (!folder) + return true; LLInventoryModel* model = &gInventory; if ("rename" == action) { @@ -133,43 +135,7 @@ bool LLInventoryAction::doToSelected(LLFolderView* folder, std::string action) return true; } -class LLDoToSelectedPanel : public object_inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLPanelObjectInventory *panel = mPtr; - LLFolderView* folder = panel->getRootFolder(); - if(!folder) return true; - - return LLInventoryAction::doToSelected(folder, userdata.asString()); - } -}; - -class LLDoToSelectedFloater : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryPanel *panel = mPtr->getPanel(); - LLFolderView* folder = panel->getRootFolder(); - if(!folder) return true; - - return LLInventoryAction::doToSelected(folder, userdata.asString()); - } -}; - -class LLDoToSelected : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryPanel *panel = mPtr; - LLFolderView* folder = panel->getRootFolder(); - if(!folder) return true; - - return LLInventoryAction::doToSelected(folder, userdata.asString()); - } -}; - -class LLNewWindow : public inventory_listener_t +struct LLNewWindow : public inventory_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -190,105 +156,10 @@ class LLNewWindow : public inventory_listener_t } }; -class LLShowFilters : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - mPtr->toggleFindOptions(); - return true; - } -}; - -class LLResetFilter : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - mPtr->resetFilters(); - return true; - } -}; - -class LLCloseAllFolders : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - mPtr->closeAllFolders(); - return true; - } -}; - -class LLCloseAllFoldersFloater : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - mPtr->getPanel()->closeAllFolders(); - return true; - } -}; - -class LLEmptyTrash : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getModel(); - if(!model) return false; - LLNotificationsUtil::add("ConfirmEmptyTrash", LLSD(), LLSD(), boost::bind(&LLEmptyTrash::callback_empty_trash, this, _1, _2)); - return true; - } - - bool callback_empty_trash(const LLSD& notification, const LLSD& response) - { - S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0) // YES - { - LLInventoryModel* model = mPtr->getModel(); - LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - model->purgeDescendentsOf(trash_id); - model->notifyObservers(); - } - return false; - } -}; - -class LLEmptyLostAndFound : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getModel(); - if(!model) return false; - LLNotificationsUtil::add("ConfirmEmptyLostAndFound", LLSD(), LLSD(), boost::bind(&LLEmptyLostAndFound::callback_empty_lost_and_found, this, _1, _2)); - return true; - } - - bool callback_empty_lost_and_found(const LLSD& notification, const LLSD& response) - { - S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0) // YES - { - LLInventoryModel* model = mPtr->getModel(); - LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - model->purgeDescendentsOf(lost_and_found_id); - model->notifyObservers(); - } - return false; - } -}; - -class LLEmptyTrashFloater : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getPanel()->getModel(); - if(!model) return false; - LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - model->purgeDescendentsOf(trash_id); - model->notifyObservers(); - return true; - } -}; - -void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type, LLFolderBridge *self = NULL) +void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, const LLSD& sdtype, LLFolderBridge *self = NULL) { + const std::string& type = sdtype.asString(); + LL_INFOS() << "self=0x" << std::hex << self << std::dec << LL_ENDL; if ("category" == type) { LLUUID category; @@ -343,31 +214,7 @@ void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type, ptr->getRootFolder()->setNeedsAutoRename(TRUE); } -class LLDoCreate : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getModel(); - if(!model) return false; - std::string type = userdata.asString(); - do_create(model, mPtr, type, LLFolderBridge::sSelf.get()); - return true; - } -}; - -class LLDoCreateFloater : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getPanel()->getModel(); - if(!model) return false; - std::string type = userdata.asString(); - do_create(model, mPtr->getPanel(), type); - return true; - } -}; - -class LLSetSortBy : public inventory_listener_t +struct LLSetSortBy : public inventory_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -426,20 +273,7 @@ class LLSetSortBy : public inventory_listener_t return true; } }; - -class LLRefreshInvModel : public inventory_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLInventoryModel* model = mPtr->getPanel()->getModel(); - if(!model) return false; - model->empty(); - LLInventoryModelBackgroundFetch::instance().start(); - return true; - } -}; - -class LLSetSearchType : public inventory_listener_t +struct LLSetSearchType : public inventory_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -451,7 +285,7 @@ class LLSetSearchType : public inventory_listener_t } }; -class LLBeginIMSession : public inventory_panel_listener_t +struct LLBeginIMSession : public inventory_panel_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -572,7 +406,7 @@ class LLBeginIMSession : public inventory_panel_listener_t } }; -class LLAttachObject : public inventory_panel_listener_t +struct LLAttachObject : public inventory_panel_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) { @@ -628,42 +462,30 @@ class LLAttachObject : public inventory_panel_listener_t void init_object_inventory_panel_actions(LLPanelObjectInventory *panel) { - (new LLDoToSelectedPanel())->registerListener(panel, "Inventory.DoToSelected"); + (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLPanelObjectInventory::getRootFolder, panel), _2))); } void init_inventory_actions(LLInventoryView *floater) { - (new LLDoToSelectedFloater())->registerListener(floater, "Inventory.DoToSelected"); - (new LLDoToSelectedFloater())->registerListener(floater, "Inventory.DoToSelected"); - (new LLCloseAllFoldersFloater())->registerListener(floater, "Inventory.CloseAllFolders"); - (new LLEmptyTrashFloater())->registerListener(floater, "Inventory.EmptyTrash"); - (new LLDoCreateFloater())->registerListener(floater, "Inventory.DoCreate"); - + (new LLBindMemberListener(floater, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLInventoryView::getRootFolder, floater), _2))); + (new LLBindMemberListener(floater, "Inventory.CloseAllFolders", boost::bind(&LLInventoryPanel::closeAllFolders, boost::bind(&LLInventoryView::getPanel, floater)))); + (new LLBindMemberListener(floater, "Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "", LLFolderType::FT_TRASH))); + (new LLBindMemberListener(floater, "Inventory.DoCreate", boost::bind(&do_create, &gInventory, boost::bind(&LLInventoryView::getPanel, floater), _2, (LLFolderBridge*)0))); (new LLNewWindow())->registerListener(floater, "Inventory.NewWindow"); - (new LLShowFilters())->registerListener(floater, "Inventory.ShowFilters"); - (new LLResetFilter())->registerListener(floater, "Inventory.ResetFilter"); + (new LLBindMemberListener(floater, "Inventory.ShowFilters", boost::bind(&LLInventoryView::toggleFindOptions, floater))); + (new LLBindMemberListener(floater, "Inventory.ResetFilter", boost::bind(&LLInventoryView::resetFilters, floater))); (new LLSetSortBy())->registerListener(floater, "Inventory.SetSortBy"); - (new LLSetSearchType())->registerListener(floater, "Inventory.SetSearchType"); } -class LLShare : public inventory_panel_listener_t -{ - bool handleEvent(LLPointer event, const LLSD& userdata) - { - LLAvatarActions::shareWithAvatars(mPtr); - return true; - } -}; - void init_inventory_panel_actions(LLInventoryPanel *panel) { - (new LLDoToSelected())->registerListener(panel, "Inventory.DoToSelected"); + (new LLBindMemberListener(panel, "Inventory.DoToSelected", boost::bind(&LLInventoryAction::doToSelected, boost::bind(&LLInventoryPanel::getRootFolder, panel), _2))); (new LLAttachObject())->registerListener(panel, "Inventory.AttachObject"); - (new LLCloseAllFolders())->registerListener(panel, "Inventory.CloseAllFolders"); - (new LLEmptyTrash())->registerListener(panel, "Inventory.EmptyTrash"); - (new LLEmptyLostAndFound())->registerListener(panel, "Inventory.EmptyLostAndFound"); - (new LLDoCreate())->registerListener(panel, "Inventory.DoCreate"); + (new LLBindMemberListener(panel, "Inventory.CloseAllFolders", boost::bind(&LLInventoryPanel::closeAllFolders, panel))); + (new LLBindMemberListener(panel, "Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH))); + (new LLBindMemberListener(panel, "Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND))); + (new LLBindMemberListener(panel, "Inventory.DoCreate", boost::bind(&do_create, &gInventory, panel, _2, boost::bind(&LLHandle::get, boost::cref(LLFolderBridge::sSelf))))); (new LLBeginIMSession())->registerListener(panel, "Inventory.BeginIMSession"); - (new LLShare())->registerListener(panel, "Inventory.Share"); + (new LLBindMemberListener(panel, "Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, panel))); } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index cab70d45e..e3cc5faae 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -207,7 +207,12 @@ const std::string& LLInvFVBridge::getName() const const std::string& LLInvFVBridge::getDisplayName() const { - return getName(); + if(mDisplayName.empty()) + { + buildDisplayName(); + } + + return mDisplayName; } // Folders have full perms @@ -226,14 +231,23 @@ LLFolderType::EType LLInvFVBridge::getPreferredType() const // Folders don't have creation dates. time_t LLInvFVBridge::getCreationDate() const { - /*LLInventoryObject* objectp = getInventoryObject(); + LLInventoryObject* objectp = getInventoryObject(); if (objectp) { return objectp->getCreationDate(); - }*/ + } return (time_t)0; } +void LLInvFVBridge::setCreationDate(time_t creation_date_utc) +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + objectp->setCreationDate(creation_date_utc); + } +} + // Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() const @@ -252,6 +266,11 @@ BOOL LLInvFVBridge::isLink() const return mIsLink; } +BOOL LLInvFVBridge::isLibraryItem() const +{ + return gInventory.isObjectDescendentOf(getUUID(),gInventory.getLibraryRootFolderID()); +} + /*virtual*/ /** * @brief Adds this item into clipboard storage @@ -587,6 +606,46 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const return TRUE; } +void disable_context_entries_if_present(LLMenuGL& menu, + const menuentry_vec_t &disabled_entries) +{ + const LLView::child_list_t *list = menu.getChildList(); + for (LLView::child_list_t::const_iterator itor = list->begin(); + itor != list->end(); + ++itor) + { + LLView *menu_item = (*itor); + std::string name = menu_item->getName(); + + // descend into split menus: + LLMenuItemBranchGL* branchp = dynamic_cast(menu_item); + if ((name == "More") && branchp) + { + disable_context_entries_if_present(*branchp->getBranch(), disabled_entries); + } + + bool found = false; + menuentry_vec_t::const_iterator itor2; + for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) + { + if (*itor2 == name) + { + found = true; + break; + } + } + + if (found) + { + menu_item->setVisible(TRUE); + // A bit of a hack so we can remember that some UI element explicitly set this to be visible + // so that some other UI element from multi-select doesn't later set this invisible. + menu_item->pushVisible(TRUE); + + menu_item->setEnabled(FALSE); + } + } +} void hide_context_entries(LLMenuGL& menu, const menuentry_vec_t &entries_to_show, const menuentry_vec_t &disabled_entries) @@ -832,6 +891,31 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } +bool get_selection_item_uuids(LLFolderView::selected_items_t& selected_items, uuid_vec_t& ids) +{ + uuid_vec_t results; + S32 non_item = 0; + for(LLFolderView::selected_items_t::iterator it = selected_items.begin(); it != selected_items.end(); ++it) + { + LLItemBridge *view_model = dynamic_cast((*it)->getListener()); + + if(view_model && view_model->getUUID().notNull()) + { + results.push_back(view_model->getUUID()); + } + else + { + non_item++; + } + } + if (non_item == 0) + { + ids = results; + return true; + } + return false; +} + void LLInvFVBridge::addTrashContextMenuOptions(menuentry_vec_t &items, menuentry_vec_t &disabled_items) { @@ -1015,7 +1099,7 @@ BOOL LLInvFVBridge::isCOFFolder() const BOOL LLInvFVBridge::isInboxFolder() const { - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false); if (inbox_id.isNull()) { @@ -1055,7 +1139,7 @@ BOOL LLInvFVBridge::isOutboxFolderDirectParent() const const LLUUID LLInvFVBridge::getOutboxFolder() const { - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); return outbox_id; } @@ -1227,17 +1311,10 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) { - LLInventoryCategory* cat = model->getCategory(uuid); - if (cat) - { - model->purgeDescendentsOf(uuid); - model->notifyObservers(); - } LLInventoryObject* obj = model->getObject(uuid); if (obj) { - model->purgeObject(uuid); - model->notifyObservers(); + remove_inventory_object(uuid, NULL); } } @@ -1493,7 +1570,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } } @@ -1580,6 +1657,16 @@ LLUIImagePtr LLItemBridge::getIcon() const } return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT); + +} + +LLUIImagePtr LLItemBridge::getIconOverlay() const +{ + if (getItem() && getItem()->getIsLinkType()) + { + return LLUI::getUIImage("inv_link_overlay.tga"); + } + return NULL; } PermissionMask LLItemBridge::getPermissionMask() const @@ -1590,24 +1677,15 @@ PermissionMask LLItemBridge::getPermissionMask() const return perm_mask; } -const std::string& LLItemBridge::getDisplayName() const +void LLItemBridge::buildDisplayName() const { - if(mDisplayName.empty()) + if(getItem()) { - buildDisplayName(getItem(), mDisplayName); - } - return mDisplayName; -} - -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) -{ - if(item) - { - name.assign(item->getName()); + mDisplayName.assign(getItem()->getName()); } else { - name.assign(LLStringUtil::null); + mDisplayName.assign(LLStringUtil::null); } } @@ -1738,13 +1816,9 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) LLViewerInventoryItem* item = getItem(); if(item && (item->getName() != new_name)) { - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); - new_item->updateServer(FALSE); - model->updateItem(new_item); - - model->notifyObservers(); + LLSD updates; + updates["name"] = new_name; + update_inventory_item(item->getUUID(),updates, NULL); } // return FALSE because we either notified observers (& therefore // rebuilt) or we didn't update. @@ -1783,17 +1857,8 @@ BOOL LLItemBridge::removeItem() { if (!item->getIsLinkType()) { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLLinkedItemIDMatches is_linked_item_match(mUUID); - gInventory.collectDescendentsIf(gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::INCLUDE_TRASH, - is_linked_item_match, - true); - - const U32 num_links = cat_array.size() + item_array.size(); + LLInventoryModel::item_array_t item_array = gInventory.collectLinksTo(mUUID); + const U32 num_links = item_array.size(); if (num_links > 0) { // Warn if the user is will break any links when deleting this item. @@ -1908,6 +1973,13 @@ BOOL LLFolderBridge::isItemMovable() const void LLFolderBridge::selectItem() { + // Have no fear: the first thing start() does is to test if everything for that folder has been fetched... + LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); +} + +void LLFolderBridge::buildDisplayName() const +{ + mDisplayName.assign(getName()); } // Iterate through a folder's children to determine if @@ -2259,6 +2331,28 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } + U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); + if (is_movable + && move_is_into_current_outfit + && descendent_items.size() > max_items_to_wear) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + + if (items.size() > max_items_to_wear) + { + // Can't move 'large' folders into current outfit: MAINT-4086 + is_movable = FALSE; + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", max_items_to_wear); + } + } if (is_movable && move_is_into_trash) { for (S32 i=0; i < descendent_items.size(); ++i) @@ -2416,32 +2510,11 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; model->collectDescendents(cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - LLAppearanceMgr::instance().linkAll(mUUID,items,NULL); + LLInventoryObject::const_object_list_t citems; + citems.insert(citems.begin(), items.begin(), items.end()); + link_inventory_array(mUUID, citems, NULL); } } - else - { -#if SUPPORT_ENSEMBLES - // BAP - should skip if dup. - if (move_is_into_current_outfit) - { - LLAppearanceMgr::instance().addEnsembleLink(inv_cat); - } - else - { - LLPointer cb = NULL; - const std::string empty_description = ""; - link_inventory_item( - gAgent.getID(), - cat_id, - mUUID, - inv_cat->getName(), - empty_description, - LLAssetType::AT_LINK_FOLDER, - cb); - } -#endif - } } else if (move_is_into_outbox && !move_is_from_outbox) { @@ -2449,7 +2522,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else { - if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(cat_id); } @@ -2540,7 +2613,7 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, return FALSE; } - BOOL accept = TRUE; + BOOL accept = FALSE; BOOL is_move = FALSE; // coming from a task. Need to figure out if the person can @@ -2573,9 +2646,9 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, is_move = TRUE; accept = TRUE; } - else + + if (!accept) { - accept = FALSE; break; } } @@ -2735,8 +2808,8 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) class LLInventoryCopyAndWearObserver : public LLInventoryObserver { public: - LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count, bool folder_added=false) : - mCatID(cat_id), mContentsCount(count), mFolderAdded(folder_added) {} + LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count, bool folder_added=false, bool replace=false) : + mCatID(cat_id), mContentsCount(count), mFolderAdded(folder_added), mReplace(replace){} virtual ~LLInventoryCopyAndWearObserver() {} virtual void changed(U32 mask); @@ -2744,6 +2817,7 @@ protected: LLUUID mCatID; int mContentsCount; bool mFolderAdded; + bool mReplace; }; @@ -2782,7 +2856,7 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask) mContentsCount) { gInventory.removeObserver(this); - LLAppearanceMgr::instance().wearInventoryCategory(category, FALSE, FALSE); + LLAppearanceMgr::instance().wearInventoryCategory(category, FALSE, !mReplace); delete this; } } @@ -2865,7 +2939,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLViewerInventoryCategory* cat = getCategory(); if(!cat) return; - remove_inventory_category_from_avatar( cat ); + LLAppearanceMgr::instance().takeOffOutfit( cat->getLinkedUUID() ); return; } else if ("purge" == action) @@ -2899,7 +2973,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); } #if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU @@ -2983,7 +3057,12 @@ LLFolderType::EType LLFolderBridge::getPreferredType() const // Icons for folders are based on the preferred type LLUIImagePtr LLFolderBridge::getIcon() const { - LLFolderType::EType preferred_type(getPreferredType()); // Singu Note: Duplicate code + LLFolderType::EType preferred_type = LLFolderType::FT_NONE; + LLViewerInventoryCategory* cat = getCategory(); + if(cat) + { + preferred_type = cat->getPreferredType(); + } return getIcon(preferred_type); } @@ -2999,8 +3078,21 @@ LLUIImagePtr LLFolderBridge::getIconOpen() const } +LLUIImagePtr LLFolderBridge::getIconOverlay() const +{ + if (getInventoryObject() && getInventoryObject()->getIsLinkType()) + { + return LLUI::getUIImage("inv_link_overlay.tga"); + } + return NULL; +} + BOOL LLFolderBridge::renameItem(const std::string& new_name) { + + LLScrollOnRenameObserver *observer = new LLScrollOnRenameObserver(mUUID, mRoot); + gInventory.addObserver(observer); + rename_category(getInventoryModel(), mUUID, new_name); // return FALSE because we either notified observers (& therefore @@ -3069,9 +3161,11 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); std::vector objects; @@ -3178,14 +3272,10 @@ void LLFolderBridge::pasteFromClipboard(bool only_copies) // [SL:KB] - Patch: Inventory-Links | Checked: 2010-04-12 (Catznip-2.2.0a) | Added: Catznip-2.0.0a else if (!only_copies && LLAssetType::lookupIsLinkType(item->getActualType())) { - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), + link_inventory_object( parent_id, - item->getName(), - item->getDescription(), - item->getActualType(), - LLPointer(NULL)); + item, + NULL); } // [/SL:KB] else @@ -3213,9 +3303,11 @@ void LLFolderBridge::pasteLinkFromClipboard() { const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); if (move_is_into_outbox) @@ -3241,28 +3333,9 @@ void LLFolderBridge::pasteLinkFromClipboard() dropToOutfit(item, move_is_into_current_outfit); } } - else if (LLInventoryCategory *cat = model->getCategory(object_id)) + else if (LLConstPointer obj = model->getObject(object_id)) { - const std::string empty_description = ""; - link_inventory_item( - gAgent.getID(), - cat->getUUID(), - parent_id, - cat->getName(), - empty_description, - LLAssetType::AT_LINK_FOLDER, - LLPointer(NULL)); - } - else if (LLInventoryItem *item = model->getItem(object_id)) - { - link_inventory_item( - gAgent.getID(), - item->getLinkedUUID(), - parent_id, - item->getName(), - item->getDescription(), - LLAssetType::AT_LINK, - LLPointer(NULL)); + link_inventory_object(parent_id, obj, LLPointer(NULL)); } } //Singu Note: Don't setCutMode(false) here, we can link now but real paste later. @@ -3292,7 +3365,7 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.size() > 0) ? TRUE : FALSE ); } -void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) +void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); @@ -3300,38 +3373,33 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); -// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) - // Fixes LL bug - mItems.clear(); - mDisabledItems.clear(); -// [/RLVa:KB] if (lost_and_found_id == mUUID) { // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); + items.push_back(std::string("Empty Lost And Found")); - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("New Script")); - mDisabledItems.push_back(std::string("New Note")); - mDisabledItems.push_back(std::string("New Gesture")); - mDisabledItems.push_back(std::string("New Clothes")); - mDisabledItems.push_back(std::string("New Body Parts")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); } if(trash_id == mUUID) { // This is the trash. - mItems.push_back(std::string("Empty Trash")); + items.push_back(std::string("Empty Trash")); } else if(isItemInTrash()) { // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - addTrashContextMenuOptions(mItems, mDisabledItems); + items.clear(); // clear any items that used to exist + addTrashContextMenuOptions(items, disabled_items); } else if(isOutboxFolder()) { - addOutboxContextMenuOptions(flags, mItems, mDisabledItems); + addOutboxContextMenuOptions(flags, items, disabled_items); } else if(isAgentInventory()) // do not allow creating in library { @@ -3344,38 +3412,28 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) if(panel && !panel->getFilterWorn()) if (!isInboxFolder() && !isOutboxFolder()) // don't allow creation in inbox or outbox { - mItems.push_back(std::string("New Folder")); - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Folder")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); } -#if SUPPORT_ENSEMBLES - // Changing folder types is an unfinished unsupported feature - // and can lead to unexpected behavior if enabled. - mItems.push_back(std::string("Change Type")); - const LLViewerInventoryCategory *cat = getCategory(); - if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) - { - mDisabledItems.push_back(std::string("Change Type")); - } -#endif - getClipboardEntries(false, mItems, mDisabledItems, flags); + getClipboardEntries(false, items, disabled_items, flags); } else { // Want some but not all of the items from getClipboardEntries for outfits. if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { - mItems.push_back(std::string("Rename")); + items.push_back(std::string("Rename")); - addDeleteContextMenuOptions(mItems, mDisabledItems); + addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } } } @@ -3405,134 +3463,24 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) { const LLUUID& library(gInventory.getLibraryRootFolderID()); if (library != mUUID && !gInventory.isObjectDescendentOf(mUUID, library)) - mItems.push_back(std::string("Move to Lost And Found")); + items.push_back(std::string("Move to Lost And Found")); } // // Preemptively disable system folder removal if more than one item selected. if ((flags & FIRST_SELECTED_ITEM) == 0) { - mDisabledItems.push_back(std::string("Delete System Folder")); + disabled_items.push_back(std::string("Delete System Folder")); } if (!isOutboxFolder() && !isItemInTrash()) // { - mItems.push_back(std::string("Share")); + items.push_back(std::string("Share")); if (!canShare()) { - mDisabledItems.push_back(std::string("Share")); + disabled_items.push_back(std::string("Share")); } } -} - -void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) -{ - // Build folder specific options back up - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - - const LLInventoryCategory* category = model->getCategory(mUUID); - if(!category) return; - - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (trash_id == mUUID) return; - if (isItemInTrash()) return; - if (!isAgentInventory()) return; - if (isOutboxFolder()) return; - - mItems.push_back(std::string("Open Folder In New Window")); - - LLFolderType::EType type = category->getPreferredType(); - const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); - // calling card related functionality for folders. -// [SL:KB] - Patch: Appearance-Misc | Checked: 2010-11-24 (Catznip-2.4) - const bool is_outfit = (type == LLFolderType::FT_OUTFIT); -// [/SL:KB] - - // Only enable calling-card related options for non-system folders. - if (!is_system_folder) - { - LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); - if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) - { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); - } - } - - if (!isItemRemovable()) - { - mDisabledItems.push_back(std::string("Delete")); - } - -#ifdef DELETE_SYSTEM_FOLDERS - if (LLFolderType::lookupIsProtectedType(type)) - { - mItems.push_back(std::string("Delete System Folder")); - } -#endif - - // wearables related functionality for folders. - //is_wearable - LLFindWearables is_wearable; - LLIsType is_object( LLAssetType::AT_OBJECT ); - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - - if (mWearables || - checkFolderForContentsOfType(model, is_wearable) || - checkFolderForContentsOfType(model, is_object) || - checkFolderForContentsOfType(model, is_gesture) ) - { - mItems.push_back(std::string("Folder Wearables Separator")); - - // Only enable add/replace outfit for non-system folders. - if (!is_system_folder) - { - if (InventoryLinksEnabled() && - // Adding an outfit onto another (versus replacing) doesn't make sense. - type != LLFolderType::FT_OUTFIT) - { - mItems.push_back(std::string("Add To Outfit")); - } - else if(!InventoryLinksEnabled()) - mItems.push_back(std::string("Wearable And Object Wear")); - - mItems.push_back(std::string("Replace Outfit")); - } - mItems.push_back(std::string("Replace Remove Separator")); - mItems.push_back(std::string("Remove From Outfit")); - if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) - { - mDisabledItems.push_back(std::string("Remove From Outfit")); - } -// if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) -// [SL:KB] - Patch: Appearance-Misc | Checked: 2010-11-24 (Catznip-2.4) - if ( ((is_outfit) && (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID))) || - ((!is_outfit) && (gAgentWearables.isCOFChangeInProgress())) ) -// [/SL:KB] - { - mDisabledItems.push_back(std::string("Replace Outfit")); - } - mItems.push_back(std::string("Outfit Separator")); - } -} - -// Flags unused -void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - sSelf.markDead(); - - mItems.clear(); - mDisabledItems.clear(); - - LL_DEBUGS() << "LLFolderBridge::buildContextMenu()" << LL_ENDL; - - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - - buildContextMenuBaseOptions(flags); - // Add menu items that are dependent on the contents of the folder. LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); if (category) @@ -3548,16 +3496,133 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down // This saves lots of time as buildContextMenu() is called a lot delete fetch; - buildContextMenuFolderOptions(flags); + buildContextMenuFolderOptions(flags, items, disabled_items); } else { // it's all on its way - add an observer, and the inventory will call done for us when everything is here. gInventory.addObserver(fetch); + } +} +} + +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) +{ + // Build folder specific options back up + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + const LLInventoryCategory* category = model->getCategory(mUUID); + if(!category) return; + + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (trash_id == mUUID) return; + if (isItemInTrash()) return; + if (!isAgentInventory()) return; + if (isOutboxFolder()) return; + + items.push_back(std::string("Open Folder In New Window")); + + LLFolderType::EType type = category->getPreferredType(); + const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); + // calling card related functionality for folders. +// [SL:KB] - Patch: Appearance-Misc | Checked: 2010-11-24 (Catznip-2.4) + const bool is_outfit = (type == LLFolderType::FT_OUTFIT); +// [/SL:KB] + + // Only enable calling-card related options for non-system folders. + if (!is_system_folder) + { + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) + { + items.push_back(std::string("Calling Card Separator")); + items.push_back(std::string("Conference Chat Folder")); + items.push_back(std::string("IM All Contacts In Folder")); } } - hide_context_entries(menu, mItems, mDisabledItems); + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Delete")); + } + +#ifdef DELETE_SYSTEM_FOLDERS + if (LLFolderType::lookupIsProtectedType(type)) + { + items.push_back(std::string("Delete System Folder")); + } +#endif + + // wearables related functionality for folders. + //is_wearable + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (mWearables || + checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) + { + items.push_back(std::string("Folder Wearables Separator")); + + // Only enable add/replace outfit for non-system folders. + if (!is_system_folder) + { + if (InventoryLinksEnabled() && + // Adding an outfit onto another (versus replacing) doesn't make sense. + !is_outfit) + { + items.push_back(std::string("Add To Outfit")); + } + else if(!InventoryLinksEnabled()) + items.push_back(std::string("Wearable And Object Wear")); + + items.push_back(std::string("Replace Outfit")); + } + items.push_back(std::string("Replace Remove Separator")); + items.push_back(std::string("Remove From Outfit")); + if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) + { + disabled_items.push_back(std::string("Remove From Outfit")); + } +// if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) +// [SL:KB] - Patch: Appearance-Misc | Checked: 2010-11-24 (Catznip-2.4) + if ( ((is_outfit) && (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID))) || + ((!is_outfit) && (gAgentWearables.isCOFChangeInProgress())) ) +// [/SL:KB] + { + disabled_items.push_back(std::string("Replace Outfit")); + } + if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) + { + disabled_items.push_back(std::string("Add To Outfit")); + } + items.push_back(std::string("Outfit Separator")); + } +} + +// Flags unused +void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + sSelf.markDead(); + + // fetch contents of this folder, as context menu can depend on contents + // still, user would have to open context menu again to see the changes + gInventory.fetchDescendentsOf(getUUID()); + + + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + LL_DEBUGS() << "LLFolderBridge::buildContextMenu()" << LL_ENDL; + + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + buildContextMenuOptions(flags, items, disabled_items); + hide_context_entries(menu, items, disabled_items); // Reposition the menu, in case we're adding items to an existing menu. menu.needsArrange(); @@ -3777,7 +3842,8 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response LLInventoryObject::object_list_t inventory_objects; object->getInventoryContents(inventory_objects); int contents_count = inventory_objects.size()-1; //subtract one for containing folder - LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded); + LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded, + cat_and_wear->mReplace); gInventory.addObserver(inventoryObserver); } @@ -3814,6 +3880,12 @@ static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_curr return FALSE; } + U32 flags = inv_item->getFlags(); + if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) + { + return FALSE; + } + if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) { return FALSE; @@ -3871,14 +3943,7 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c else { LLPointer cb = NULL; - link_inventory_item( - gAgent.getID(), - inv_item->getLinkedUUID(), - mUUID, - inv_item->getName(), - inv_item->getDescription(), - LLAssetType::AT_LINK, - cb); + link_inventory_object(mUUID, LLConstPointer(inv_item), cb); } } @@ -3901,10 +3966,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_favorites = (mUUID == favorites_id); - const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id); @@ -4095,7 +4162,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, else { // set up observer to select item once drag and drop from inbox is complete - if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(inv_item->getUUID()); } @@ -4472,6 +4539,23 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } +void LLSoundBridge::performAction(LLInventoryModel* model, std::string action) +{ + if ("sound_play" == action) + { + LLViewerInventoryItem* item = getItem(); + if(item) + { + send_sound_trigger(item->getAssetUUID(), 1.f); + } + } + else if ("open" == action) + { + openSoundPreview((void*)this); + } + else LLItemBridge::performAction(model, action); +} + // +=================================================+ // | LLLandmarkBridge | // +=================================================+ @@ -4804,7 +4888,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) LLInventoryItem* item = getItem(); BOOL good_card = (item - && (item->getCreatorUUID().notNull()) + && (LLUUID::null != item->getCreatorUUID()) && (item->getCreatorUUID() != gAgent.getID())); BOOL user_online = FALSE; if (item) @@ -5588,10 +5672,10 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { LLPointer new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); model->notifyObservers(); + buildDisplayName(); if (isAgentAvatarValid()) { @@ -5641,118 +5725,6 @@ LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, mInvType = inv_type; } -void remove_inventory_category_from_avatar( LLInventoryCategory* category ) -{ - if(!category) return; - LL_DEBUGS() << "remove_inventory_category_from_avatar( " << category->getName() - << " )" << LL_ENDL; - if (LLFloaterCustomize::instanceExists()) - { - LLFloaterCustomize::getInstance()->askToSaveIfDirty(boost::bind(&remove_inventory_category_from_avatar_step2,_1,category->getUUID())); - } - else - remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() ); -} - -void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id) -{ - - // Find all the wearables that are in the category's subtree. - LL_DEBUGS() << "remove_inventory_category_from_avatar_step2()" << LL_ENDL; - if(proceed) - { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLFindWearables is_wearable; - gInventory.collectDescendentsIf(category_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_wearable, - true); - S32 i; - S32 wearable_count = item_array.size(); - - LLInventoryModel::cat_array_t obj_cat_array; - LLInventoryModel::item_array_t obj_item_array; - LLIsType is_object( LLAssetType::AT_OBJECT ); - gInventory.collectDescendentsIf(category_id, - obj_cat_array, - obj_item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_object, - true); - S32 obj_count = obj_item_array.size(); - - // Find all gestures in this folder - LLInventoryModel::cat_array_t gest_cat_array; - LLInventoryModel::item_array_t gest_item_array; - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - gInventory.collectDescendentsIf(category_id, - gest_cat_array, - gest_item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_gesture, - true); - S32 gest_count = gest_item_array.size(); - - if (wearable_count > 0) //Loop through wearables. If worn, remove. - { - for(i = 0; i < wearable_count; ++i) - { - LLViewerInventoryItem *item = item_array.at(i); - if (item->getType() == LLAssetType::AT_BODYPART) - continue; - if (gAgent.isTeen() && item->isWearableType() && - (item->getWearableType() == LLWearableType::WT_UNDERPANTS || item->getWearableType() == LLWearableType::WT_UNDERSHIRT)) - continue; - if (get_is_item_worn(item->getUUID())) - { -// [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.0c) | Modified: RLVa-0.2.2a -// if ( (rlv_handler_t::isEnabled()) && (!gRlvWearableLocks.canRemove(item)) ) -// continue; -// [/RLVa:KB] -// LLWearableList::instance().getAsset(item->getAssetUUID(), -// item->getName(), -// item->getType(), -// LLWearableBridge::onRemoveFromAvatarArrived, -// new OnRemoveStruct(item->getLinkedUUID())); -// [SL:KB] - Patch: Appearance-RemoveWearableFromAvatar | Checked: 2010-08-13 (Catznip-3.0.0a) | Added: Catznip-2.1.1d - LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID()); -// [/SL:KB] - } - } - } - - if (obj_count > 0) - { - for(i = 0; i < obj_count; ++i) - { - LLViewerInventoryItem *obj_item = obj_item_array.at(i); - if (get_is_item_worn(obj_item->getUUID())) - { - LLVOAvatarSelf::detachAttachmentIntoInventory(obj_item->getLinkedUUID()); - } - } - } - - if (gest_count > 0) - { - for(i = 0; i < gest_count; ++i) - { - LLViewerInventoryItem *gest_item = gest_item_array.at(i); - if (get_is_item_worn(gest_item->getUUID())) - { - LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() ); - gInventory.updateItem( gest_item ); - gInventory.notifyObservers(); - } - - } - } - } -} - BOOL LLWearableBridge::renameItem(const std::string& new_name) { if (get_is_item_worn(mUUID)) @@ -5977,9 +5949,9 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(!is_worn || !gAgentWearables.canMoveWearable(item->getUUID(),true)) disabled_items.push_back(std::string("Wearable Move Back")); -// if (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) +// if (!gAgentWearables.canAddWearable(mWearableType)) // [SL:KB] - Patch: Appearance-WearableDuplicateAssets | Checked: 2011-07-24 (Catznip-2.6.0e) | Added: Catznip-2.6.0e - if ( (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) || + if (!gAgentWearables.canAddWearable(mWearableType) || (gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) ) // [/SL:KB] { @@ -6744,9 +6716,8 @@ LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_ /************************************************************************/ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - LLFolderBridge::buildContextMenu(menu, flags); - - menuentry_vec_t disabled_items, items = getMenuItems(); + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); @@ -6781,4 +6752,22 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( return new_listener; } +LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge() +{ +} + +void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) +{ + uuid_vec_t ids; + menuentry_vec_t disabled_items; + if (get_selection_item_uuids(selected_items, ids)) + { + if (!LLAppearanceMgr::instance().canAddWearables(ids)) + { + disabled_items.push_back(std::string("Wearable Add")); + } + } + disable_context_entries_if_present(menu, disabled_items); +} + // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 7dd427a66..1ea6b4724 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -29,6 +29,7 @@ #include "llcallingcard.h" #include "llfloaterproperties.h" +#include "llfolderview.h" #include "llfoldervieweventlistener.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" @@ -77,7 +78,7 @@ public: // LLInvFVBridge functionality //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } - virtual void clearDisplayName() {} + virtual void clearDisplayName() { mDisplayName.clear(); } virtual const std::string& getPrefix() { return LLStringUtil::null; } virtual void restoreItem() {} virtual void restoreToWorld() {} @@ -87,9 +88,11 @@ public: //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + virtual PermissionMask getPermissionMask() const; virtual LLFolderType::EType getPreferredType() const; virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} @@ -102,6 +105,7 @@ public: virtual BOOL isItemMovable() const; virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; + virtual BOOL isLibraryItem() const; //virtual BOOL removeItem() = 0; virtual void removeBatch(std::vector& batch); virtual void move(LLFolderViewEventListener* new_parent_bridge) {} @@ -148,7 +152,6 @@ protected: BOOL isAgentInventory() const; // false if lost or in the inventory library BOOL isCOFFolder() const; // true if COF or descendant of BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox - BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox BOOL isOutboxFolderDirectParent() const; const LLUUID getOutboxFolder() const; @@ -162,13 +165,18 @@ protected: const LLUUID& new_parent, BOOL restamp); void removeBatchNoCheck(std::vector& batch); +public: + BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox protected: LLHandle mInventoryPanel; LLFolderView* mRoot; const LLUUID mUUID; // item id LLInventoryType::EType mInvType; bool mIsLink; + mutable std::string mDisplayName; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); + virtual void buildDisplayName() const {} }; class AIFilePicker; @@ -206,7 +214,6 @@ public: virtual void restoreToWorld(); virtual void gotoItem(); virtual LLUIImagePtr getIcon() const; - virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; virtual PermissionMask getPermissionMask() const; @@ -217,19 +224,15 @@ public: virtual BOOL isItemCopyable() const; virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - + virtual LLUIImagePtr getIconOverlay() const; static void showFloaterImagePreview(LLInventoryItem* item, AIFilePicker* filepicker); - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } - LLViewerInventoryItem* getItem() const; protected: BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; - static void buildDisplayName(LLInventoryItem* item, std::string& name); - - mutable std::string mDisplayName; + virtual void buildDisplayName() const; }; class LLFolderBridge : public LLInvFVBridge @@ -246,6 +249,8 @@ public: BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); + virtual void buildDisplayName() const; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -256,6 +261,8 @@ public: virtual LLFolderType::EType getPreferredType() const; virtual LLUIImagePtr getIcon() const; virtual LLUIImagePtr getIconOpen() const; + virtual LLUIImagePtr getIconOverlay() const; + static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual BOOL renameItem(const std::string& new_name); @@ -285,8 +292,8 @@ public: LLHandle getHandle() { mHandle.bind(this); return mHandle; } protected: - void buildContextMenuBaseOptions(U32 flags); - void buildContextMenuFolderOptions(U32 flags); + 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); //-------------------------------------------------------------------- // Menu callbacks @@ -315,8 +322,6 @@ protected: void modifyOutfit(BOOL append); void determineFolderType(); - menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items - void dropToFavorites(LLInventoryItem* inv_item); void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); @@ -331,8 +336,6 @@ private: bool mCallingCards; bool mWearables; - menuentry_vec_t mItems; - menuentry_vec_t mDisabledItems; LLRootHandle mHandle; }; @@ -365,6 +368,7 @@ public: virtual void openItem(); virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void performAction(LLInventoryModel* model, std::string action); static void openSoundPreview(void*); }; @@ -660,4 +664,11 @@ void hide_context_entries(LLMenuGL& menu, const menuentry_vec_t &entries_to_show, const menuentry_vec_t &disabled_entries); +class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel +{ +public: + LLFolderViewGroupedItemBridge(); + virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu); +}; + #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index c019414e4..124fbffe1 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -154,12 +154,9 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s return; } - LLPointer new_cat = new LLViewerInventoryCategory(cat); - new_cat->rename(new_name); - new_cat->updateServer(FALSE); - model->updateCategory(new_cat); - - model->notifyObservers(); + LLSD updates; + updates["name"] = new_name; + update_inventory_category(cat_id, updates, NULL); } void copy_inventory_category(LLInventoryModel* model, @@ -740,6 +737,13 @@ bool LLIsOfAssetType::operator()(LLInventoryCategory* cat, LLInventoryItem* item return FALSE; } +bool LLIsValidItemLink::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem) return false; + return (vitem->getActualType() == LLAssetType::AT_LINK && !vitem->getIsBrokenLink()); +} + bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if(mType == LLAssetType::AT_CATEGORY) diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index f42543e52..e986eabda 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -186,6 +186,13 @@ protected: LLAssetType::EType mType; }; +class LLIsValidItemLink : public LLInventoryCollectFunctor +{ +public: + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + class LLIsTypeWithPermissions : public LLInventoryCollectFunctor { public: diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 7a71ba201..d87fad1c6 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -48,6 +48,7 @@ #include "llcallbacklist.h" #include "llvoavatarself.h" #include "llgesturemgr.h" +#include "llsdutil.h" #include #include "statemachine/aievent.h" @@ -113,17 +114,7 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { S32 descendents_server = c->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - mModel->getDirectDescendentsOf( - c->getUUID(), - cats, - items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->size() + items->size(); - } + S32 descendents_actual = c->getViewerDescendentCount(); if(descendents_server == descendents_actual) { mCachedCatIDs.insert(c->getUUID()); @@ -143,23 +134,24 @@ LLInventoryModel gInventory; // Default constructor LLInventoryModel::LLInventoryModel() -: mModifyMask(LLInventoryObserver::ALL), - mChangedItemIDs(), - mCategoryMap(), - mItemMap(), - mCategoryLock(), - mItemLock(), - mLastItem(NULL), - mParentChildCategoryTree(), - mParentChildItemTree(), - mObservers(), +: // These are now ordered, keep them that way. + mBacklinkMMap(), + mIsAgentInvUsable(false), mRootFolderID(), mLibraryRootFolderID(), mLibraryOwnerID(), + mCategoryMap(), + mItemMap(), + mParentChildCategoryTree(), + mParentChildItemTree(), + mLastItem(NULL), mIsNotifyObservers(FALSE), - mIsAgentInvUsable(false) -{ -} + mModifyMask(LLInventoryObserver::ALL), + mChangedItemIDs(), + mObservers(), + mCategoryLock(), + mItemLock() +{} // Destroys the object LLInventoryModel::~LLInventoryModel() @@ -282,6 +274,23 @@ const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LL return NULL; } +bool LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const +{ + LLInventoryObject *object = getObject(object_id); + while (object && object->getParentUUID().notNull()) + { + LLInventoryObject *parent_object = getObject(object->getParentUUID()); + if (!parent_object) + { + LL_WARNS(LOG_INV) << "unable to trace topmost ancestor, missing item for uuid " << object->getParentUUID() << LL_ENDL; + return false; + } + object = parent_object; + } + result = object->getUUID(); + return true; +} + // Get the object by id. Returns NULL if not found. LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const { @@ -466,16 +475,12 @@ void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::E } } -// findCategoryUUIDForType() 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: This will create a new -// inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder, - bool find_in_library) +const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( + LLFolderType::EType preferred_type, + bool create_folder, + const LLUUID& root_id) { LLUUID rv = LLUUID::null; - const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { rv = root_id; @@ -491,14 +496,17 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe { if(cats->at(i)->getPreferredType() == preferred_type) { - rv = cats->at(i)->getUUID(); - break; + const LLUUID& folder_id = cats->at(i)->getUUID(); + if (rv.isNull() || folder_id < rv) + { + rv = folder_id; + } } } } } - if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + if(rv.isNull() && isInventoryUsable() && create_folder) { if(root_id.notNull()) { @@ -508,6 +516,20 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe return rv; } +// findCategoryUUIDForType() 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: This will create a new +// inventory category on the fly if one does not exist. +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID()); +} + +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID()); +} + LLUUID LLInventoryModel::findCategoryByName(std::string name) { LLUUID root_id = gInventory.getRootFolderID(); @@ -532,13 +554,12 @@ LLUUID LLInventoryModel::findCategoryByName(std::string name) class LLCreateInventoryCategoryResponder : public LLHTTPClient::ResponderWithResult { + LOG_CLASS(LLCreateInventoryCategoryResponder); public: - LLCreateInventoryCategoryResponder(LLInventoryModel* model, - void (*callback)(const LLSD&, void*), - void* user_data) : - mModel(model), - mCallback(callback), - mData(user_data) + LLCreateInventoryCategoryResponder(LLInventoryModel* model, + boost::optional callback): + mModel(model), + mCallback(callback) { } @@ -550,26 +571,31 @@ public: /*virtual*/ void httpSuccess(void) { //Server has created folder. - - LLUUID category_id = mContent["folder_id"].asUUID(); - - + const LLSD& content = getContent(); + if (!content.isMap() || !content.has("folder_id")) + { + failureResult(400, "Malformed response contents", content); + return; + } + LLUUID category_id = content["folder_id"].asUUID(); + + LL_DEBUGS(LOG_INV) << ll_pretty_print_sd(content) << LL_ENDL; // Add the category to the internal representation LLPointer cat = - new LLViewerInventoryCategory( category_id, - mContent["parent_id"].asUUID(), - (LLFolderType::EType)mContent["type"].asInteger(), - mContent["name"].asString(), + new LLViewerInventoryCategory( category_id, + content["parent_id"].asUUID(), + (LLFolderType::EType)content["type"].asInteger(), + content["name"].asString(), gAgent.getID() ); cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); cat->setDescendentCount(0); LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); mModel->accountForUpdate(update); mModel->updateCategory(cat); - - if (mCallback && mData) + + if (mCallback) { - mCallback(mContent, mData); + mCallback.get()(category_id); } } @@ -578,8 +604,7 @@ public: /*virtual*/ char const* getName(void) const { return "LLCreateInventoryCategoryResponder"; } private: - void (*mCallback)(const LLSD&, void*); - void* mData; + boost::optional mCallback; LLInventoryModel* mModel; }; @@ -590,9 +615,9 @@ private: LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& pname, - void (*callback)(const LLSD&, void*), //Default to NULL - void* user_data) //Default to NULL + boost::optional callback) { + LLUUID id; if(!isInventoryUsable()) { @@ -616,35 +641,33 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, { name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } - - if ( callback && user_data ) //callback required for acked message. + + LLViewerRegion* viewer_region = gAgent.getRegion(); + std::string url; + if ( viewer_region ) + url = viewer_region->getCapability("CreateInventoryCategory"); + + if (!url.empty() && callback.get_ptr()) { - LLViewerRegion* viewer_region = gAgent.getRegion(); - std::string url; - if ( viewer_region ) - url = viewer_region->getCapability("CreateInventoryCategory"); + //Let's use the new capability. + + LLSD request, body; + body["folder_id"] = id; + body["parent_id"] = parent_id; + body["type"] = (LLSD::Integer) preferred_type; + body["name"] = name; + + request["message"] = "CreateInventoryCategory"; + request["payload"] = body; - if (!url.empty()) - { - //Let's use the new capability. + LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; + // viewer_region->getCapAPI().post(request); + LLHTTPClient::post( + url, + body, + new LLCreateInventoryCategoryResponder(this, callback) ); -// LLSD request; - LLSD body; - body["folder_id"] = id; - body["parent_id"] = parent_id; - body["type"] = (LLSD::Integer) preferred_type; - body["name"] = name; - -// request["message"] = "CreateInventoryCategory"; -// request["payload"] = body; - -// viewer_region->getCapAPI().post(request); - LLHTTPClient::post( - url, - body, - new LLCreateInventoryCategoryResponder(this, callback, user_data) ); - return LLUUID::null; - } + return LLUUID::null; } // Add the category to the internal representation @@ -671,6 +694,52 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return id; } +// This is optimized for the case that we just want to know whether a +// category has any immediate children meeting a condition, without +// needing to recurse or build up any lists. +bool LLInventoryModel::hasMatchingDirectDescendent(const LLUUID& cat_id, + LLInventoryCollectFunctor& filter, bool follow_folder_links) +{ + LLInventoryModel::cat_array_t *cats; + LLInventoryModel::item_array_t *items; + getDirectDescendentsOf(cat_id, cats, items); + if (cats) + { + for (LLInventoryModel::cat_array_t::const_iterator it = cats->begin(); + it != cats->end(); ++it) + { + if (filter(*it,NULL)) + { + return true; + } + } + } + if (items) + { + for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); + it != items->end(); ++it) + { + if (filter(NULL,*it)) + { + return true; + } + // Follow folder links recursively. Currently never goes more + // than one level deep (for current outfit support) + // Note: if making it fully recursive, need more checking against infinite loops. + else if (follow_folder_links && *it && it->get()->getActualType() == LLAssetType::AT_LINK_FOLDER) + { + LLViewerInventoryCategory *linked_cat = it->get()->getLinkedCategory(); + if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT) + { + if (hasMatchingDirectDescendent(linked_cat->getUUID(), filter)) + return true; + } + } + } + } + return false; +} + // Starting with the object specified, add its descendents to the // array provided, but do not add the inventory object specified by // id. There is no guaranteed order. Neither array will be erased @@ -789,26 +858,7 @@ void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask) if (!obj || obj->getIsLinkType()) return; - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLLinkedItemIDMatches is_linked_item_match(object_id); - collectDescendentsIf(gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::INCLUDE_TRASH, - is_linked_item_match); - if (cat_array.empty() && item_array.empty()) - { - return; - } - for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin(); - cat_iter != cat_array.end(); - cat_iter++) - { - LLViewerInventoryCategory *linked_cat = (*cat_iter); - addChangedMask(mask, linked_cat->getUUID()); - }; - + LLInventoryModel::item_array_t item_array = collectLinksTo(object_id); for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); iter != item_array.end(); iter++) @@ -836,17 +886,29 @@ LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL; } -LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id, +LLInventoryModel::item_array_t LLInventoryModel::collectLinksTo(const LLUUID& id, const LLUUID& start_folder_id) { + // Get item list via collectDescendents (slow!) item_array_t items; - LLInventoryModel::cat_array_t cat_array; - LLLinkedItemIDMatches is_linked_item_match(id); - collectDescendentsIf((start_folder_id == LLUUID::null ? gInventory.getRootFolderID() : start_folder_id), - cat_array, - items, - LLInventoryModel::INCLUDE_TRASH, - is_linked_item_match); + const LLInventoryObject *obj = getObject(id); + // FIXME - should be as in next line, but this is causing a + // stack-smashing crash of cause TBD... check in the REBUILD code. + //if (obj && obj->getIsLinkType()) + if (!obj || obj->getIsLinkType()) + return items; + + std::pair range = mBacklinkMMap.equal_range(id); + for (backlink_mmap_t::iterator it = range.first; it != range.second; ++it) + { + LLViewerInventoryItem *item = getItem(it->second); + if (item) + { + if(start_folder_id.isNull() || isObjectDescendentOf(it->second, start_folder_id)) + items.push_back(item); + } + } + return items; } @@ -863,9 +925,8 @@ bool LLInventoryModel::isInventoryUsable() const // Calling this method with an inventory item will either change an // existing item with a matching item_id, or will add the item to the // current inventory. -U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) +U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) { - U32 mask = LLInventoryObserver::NONE; if(item->getUUID().isNull()) { return mask; @@ -885,7 +946,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) } #endif - LLViewerInventoryItem* old_item = getItem(item->getUUID()); + LLPointer old_item = getItem(item->getUUID()); LLPointer new_item; if(old_item) { @@ -1048,7 +1109,7 @@ LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLU // Calling this method with an inventory category will either change // an existing item with the matching id, or it will add the category. -void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) +void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 mask) { if(cat->getUUID().isNull()) { @@ -1065,7 +1126,6 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) if(old_cat) { // We already have an old category, modify it's values - U32 mask = LLInventoryObserver::NONE; LLUUID old_parent_id = old_cat->getParentUUID(); LLUUID new_parent_id = cat->getParentUUID(); if(old_parent_id != new_parent_id) @@ -1083,6 +1143,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) cat_array->push_back(old_cat); } mask |= LLInventoryObserver::STRUCTURE; + mask |= LLInventoryObserver::INTERNAL; } if(old_cat->getName() != cat->getName()) { @@ -1107,8 +1168,8 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) } // make space in the tree for this category's children. - llassert_always(mCategoryLock[new_cat->getUUID()] == false); - llassert_always(mItemLock[new_cat->getUUID()] == false); + llassert_always(mCategoryLock[LLUUID(new_cat->getUUID())] == false); + llassert_always(mItemLock[LLUUID(new_cat->getUUID())] == false); cat_array_t* catsp = new cat_array_t; item_array_t* itemsp = new item_array_t; mParentChildCategoryTree[new_cat->getUUID()] = catsp; @@ -1183,7 +1244,7 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item, LLPointer new_item = new LLViewerInventoryItem(item); new_item->setParent(new_parent_id); // - if(gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getRootFolderID())) + if(isObjectDescendentOf(item->getUUID(), gInventory.getRootFolderID())) // new_item->updateParentOnServer(restamp); updateItem(new_item); @@ -1221,8 +1282,186 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, notifyObservers(); } +// Does not appear to be used currently. +void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version) +{ + U32 mask = LLInventoryObserver::NONE; + + LLPointer item = gInventory.getItem(item_id); + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (item ? item->getName() : "(NOT FOUND)") << LL_ENDL; + if(item) + { + for (LLSD::map_const_iterator it = updates.beginMap(); + it != updates.endMap(); ++it) + { + if (it->first == "name") + { + LL_INFOS(LOG_INV) << "Updating name from " << item->getName() << " to " << it->second.asString() << LL_ENDL; + item->rename(it->second.asString()); + mask |= LLInventoryObserver::LABEL; + } + else if (it->first == "desc") + { + LL_INFOS(LOG_INV) << "Updating description from " << item->getActualDescription() + << " to " << it->second.asString() << LL_ENDL; + item->setDescription(it->second.asString()); + } + else + { + LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL; + } + } + mask |= LLInventoryObserver::INTERNAL; + addChangedMask(mask, item->getUUID()); + if (update_parent_version) + { + // Descendent count is unchanged, but folder version incremented. + LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0); + accountForUpdate(up); + } + notifyObservers(); // do we want to be able to make this optional? + } +} + +// Not used? +void LLInventoryModel::onCategoryUpdated(const LLUUID& cat_id, const LLSD& updates) +{ + U32 mask = LLInventoryObserver::NONE; + + LLPointer cat = gInventory.getCategory(cat_id); + LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (cat ? cat->getName() : "(NOT FOUND)") << LL_ENDL; + if(cat) + { + for (LLSD::map_const_iterator it = updates.beginMap(); + it != updates.endMap(); ++it) + { + if (it->first == "name") + { + LL_INFOS(LOG_INV) << "Updating name from " << cat->getName() << " to " << it->second.asString() << LL_ENDL; + cat->rename(it->second.asString()); + mask |= LLInventoryObserver::LABEL; + } + else + { + LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL; + } + } + mask |= LLInventoryObserver::INTERNAL; + addChangedMask(mask, cat->getUUID()); + notifyObservers(); // do we want to be able to make this optional? + } +} + +// Update model after descendents have been purged. +void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links) +{ + LLPointer cat = getCategory(object_id); + if (cat.notNull()) + { + // do the cache accounting + S32 descendents = cat->getDescendentCount(); + if(descendents > 0) + { + LLInventoryModel::LLCategoryUpdate up(object_id, -descendents); + accountForUpdate(up); + } + + // we know that descendent count is 0, however since the + // accounting may actually not do an update, we should force + // it here. + cat->setDescendentCount(0); + + // unceremoniously remove anything we have locally stored. + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + collectDescendents(object_id, + categories, + items, + LLInventoryModel::INCLUDE_TRASH); + S32 count = items.size(); + + LLUUID uu_id; + for(S32 i = 0; i < count; ++i) + { + uu_id = items.at(i)->getUUID(); + + // This check prevents the deletion of a previously deleted item. + // This is necessary because deletion is not done in a hierarchical + // order. The current item may have been already deleted as a child + // of its deleted parent. + if (getItem(uu_id)) + { + deleteObject(uu_id, fix_broken_links); + } + } + + count = categories.size(); + // Slightly kludgy way to make sure categories are removed + // only after their child categories have gone away. + + // FIXME: Would probably make more sense to have this whole + // descendent-clearing thing be a post-order recursive + // function to get the leaf-up behavior automatically. + S32 deleted_count; + S32 total_deleted_count = 0; + do + { + deleted_count = 0; + for(S32 i = 0; i < count; ++i) + { + uu_id = categories.at(i)->getUUID(); + if (getCategory(uu_id)) + { + cat_array_t* cat_list = getUnlockedCatArray(uu_id); + if (!cat_list || (cat_list->size() == 0)) + { + deleteObject(uu_id, fix_broken_links); + deleted_count++; + } + } + } + total_deleted_count += deleted_count; + } + while (deleted_count > 0); + if (total_deleted_count != count) + { + LL_WARNS(LOG_INV) << "Unexpected count of categories deleted, got " + << total_deleted_count << " expected " << count << LL_ENDL; + } + //gInventory.validate(); + } +} + +// Update model after an item is confirmed as removed from +// server. Works for categories or items. +void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool fix_broken_links, bool update_parent_version, bool do_notify_observers) +{ + LLPointer obj = getObject(object_id); + if(obj) + { + if (getCategory(object_id)) + { + // For category, need to delete/update all children first. + onDescendentsPurgedFromServer(object_id, fix_broken_links); + } + + + // From item/cat removeFromServer() + if (update_parent_version) + { + LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1); + accountForUpdate(up); + } + + // From purgeObject() + LLPreview::hide(object_id); + deleteObject(object_id, fix_broken_links, do_notify_observers); + } +} + + // Delete a particular inventory object by ID. -void LLInventoryModel::deleteObject(const LLUUID& id) +void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, bool do_notify_observers) { LL_DEBUGS(LOG_INV) << "LLInventoryModel::deleteObject()" << LL_ENDL; LLPointer obj = getObject(id); @@ -1241,151 +1480,76 @@ void LLInventoryModel::deleteObject(const LLUUID& id) item_array_t* item_list = getUnlockedItemArray(parent_id); if(item_list) { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); + LLPointer item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); vector_replace_with_last(*item_list, item); } cat_array_t* cat_list = getUnlockedCatArray(parent_id); if(cat_list) { - LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); + LLPointer cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); vector_replace_with_last(*cat_list, cat); } item_list = getUnlockedItemArray(id); if(item_list) { + if (item_list->size()) + { + LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child items" << LL_ENDL; + } delete item_list; mParentChildItemTree.erase(id); } cat_list = getUnlockedCatArray(id); if(cat_list) { + if (cat_list->size()) + { + LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child cats" << LL_ENDL; + } delete cat_list; mParentChildCategoryTree.erase(id); } addChangedMask(LLInventoryObserver::REMOVE, id); - obj = NULL; // delete obj - updateLinkedObjectsFromPurge(id); - gInventory.notifyObservers(); -} -// Delete a particular inventory item by ID, and remove it from the server. -void LLInventoryModel::purgeObject(const LLUUID &id) -{ - LL_DEBUGS() << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << LL_ENDL; - LLPointer obj = getObject(id); - if(obj) + bool is_link_type = obj->getIsLinkType(); + if (is_link_type) { - obj->removeFromServer(); - LLPreview::hide(id); - deleteObject(id); + removeBacklinkInfo(obj->getUUID(), obj->getLinkedUUID()); + } + + // Can't have links to links, so there's no need for this update + // if the item removed is a link. Can also skip if source of the + // update is getting broken link info separately. + obj = NULL; // delete obj + if (fix_broken_links && !is_link_type) + { + updateLinkedObjectsFromPurge(id); + } + if (do_notify_observers) + { + notifyObservers(); } } void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id) { - LLInventoryModel::item_array_t item_array = collectLinkedItems(baseobj_id); + LLInventoryModel::item_array_t item_array = collectLinksTo(baseobj_id); // REBUILD is expensive, so clear the current change list first else // everything else on the changelist will also get rebuilt. - gInventory.notifyObservers(); - for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); - iter != item_array.end(); - iter++) + if (item_array.size() > 0) { - const LLViewerInventoryItem *linked_item = (*iter); - const LLUUID &item_id = linked_item->getUUID(); - if (item_id == baseobj_id) continue; - addChangedMask(LLInventoryObserver::REBUILD, item_id); - } - gInventory.notifyObservers(); -} - -// This is a method which collects the descendents of the id -// provided. If the category is not found, no action is -// taken. This method goes through the long winded process of -// cancelling any calling cards, removing server representation of -// folders, items, etc in a fairly efficient manner. -void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) -{ - LLPointer cat = getCategory(id); - if (cat.notNull()) - { - if (LLInventoryClipboard::instance().hasContents() && LLInventoryClipboard::instance().isCutMode()) + notifyObservers(); + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) { - // Something on the clipboard is in "cut mode" and needs to be preserved - LL_INFOS() << "LLInventoryModel::purgeDescendentsOf " << cat->getName() - << " iterate and purge non hidden items" << LL_ENDL; - cat_array_t* categories; - item_array_t* items; - // Get the list of direct descendants in that category passed as argument - getDirectDescendentsOf(id, categories, items); - std::vector list_uuids; - // Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently) - // Note: we need to do that shallow copy as purging things will invalidate the categories or items lists - for (cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it) - { - list_uuids.push_back((*it)->getUUID()); - } - for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it) - { - list_uuids.push_back((*it)->getUUID()); - } - // Iterate through the list and only purge the UUIDs that are not on the clipboard - for (std::vector::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it) - { - if (!LLInventoryClipboard::instance().isOnClipboard(*it)) - { - purgeObject(*it); - } - } - } - else - { - // Fast purge - // do the cache accounting - LL_INFOS() << "LLInventoryModel::purgeDescendentsOf " << cat->getName() - << LL_ENDL; - S32 descendents = cat->getDescendentCount(); - if(descendents > 0) - { - LLCategoryUpdate up(id, -descendents); - accountForUpdate(up); - } - - // we know that descendent count is 0, however since the - // accounting may actually not do an update, we should force - // it here. - cat->setDescendentCount(0); - - // send it upstream - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("PurgeInventoryDescendents"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("FolderID", id); - gAgent.sendReliableMessage(); - - // unceremoniously remove anything we have locally stored. - cat_array_t categories; - item_array_t items; - collectDescendents(id, - categories, - items, - INCLUDE_TRASH); - S32 count = items.size(); - for(S32 i = 0; i < count; ++i) - { - deleteObject(items.at(i)->getUUID()); - } - - count = categories.size(); - for(S32 i = 0; i < count; ++i) - { - deleteObject(categories.at(i)->getUUID()); - } + const LLViewerInventoryItem *linked_item = (*iter); + const LLUUID &item_id = linked_item->getUUID(); + if (item_id == baseobj_id) continue; + addChangedMask(LLInventoryObserver::REBUILD, item_id); } + notifyObservers(); } } @@ -1440,6 +1604,7 @@ void LLInventoryModel::notifyObservers() mModifyMask = LLInventoryObserver::NONE; mChangedItemIDs.clear(); + mAddedItemIDs.clear(); mIsNotifyObservers = FALSE; } @@ -1453,102 +1618,52 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) // (which is in the process of processing the list of items marked for change). // This means the change may fail to be processed. LL_WARNS(LOG_INV) << "Adding changed mask within notify observers! Change will likely be lost." << LL_ENDL; + LLViewerInventoryItem *item = getItem(referent); + if (item) + { + LL_WARNS(LOG_INV) << "Item " << item->getName() << LL_ENDL; + } + else + { + LLViewerInventoryCategory *cat = getCategory(referent); + if (cat) + { + LL_WARNS(LOG_INV) << "Category " << cat->getName() << LL_ENDL; + } + } } mModifyMask |= mask; if (referent.notNull()) { mChangedItemIDs.insert(referent); - } - - // Update all linked items. Starting with just LABEL because I'm - // not sure what else might need to be accounted for this. - if (mModifyMask & LLInventoryObserver::LABEL) - { - addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); - } -} -// If we get back a normal response, handle it here -void LLInventoryModel::fetchInventoryResponder::httpSuccess(void) -{ - start_new_inventory_observer(); + if (mask & LLInventoryObserver::ADD) + { + mAddedItemIDs.insert(referent); + } - /*LLUUID agent_id; - agent_id = mContent["agent_id"].asUUID(); - if(agent_id != gAgent.getID()) - { - LL_WARNS() << "Got a inventory update for the wrong agent: " << agent_id - << LL_ENDL; - return; - }*/ - item_array_t items; - update_map_t update; - S32 count = mContent["items"].size(); - LLUUID folder_id; - // Does this loop ever execute more than once? - for(S32 i = 0; i < count; ++i) - { - LLPointer titem = new LLViewerInventoryItem; - titem->unpackMessage(mContent["items"][i]); - - LL_DEBUGS() << "LLInventoryModel::messageUpdateCore() item id:" - << titem->getUUID() << LL_ENDL; - items.push_back(titem); - // examine update for changes. - LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); - if(itemp) + // Update all linked items. Starting with just LABEL because I'm + // not sure what else might need to be accounted for this. + if (mask & LLInventoryObserver::LABEL) { - if(titem->getParentUUID() == itemp->getParentUUID()) - { - update[titem->getParentUUID()]; - } - else - { - ++update[titem->getParentUUID()]; - --update[itemp->getParentUUID()]; - } - } - else - { - ++update[titem->getParentUUID()]; - } - if (folder_id.isNull()) - { - folder_id = titem->getParentUUID(); + addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); } } - - U32 changes = 0x0; - //as above, this loop never seems to loop more than once per call - for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) - { - changes |= gInventory.updateItem(*it); - } - gInventory.notifyObservers(); - gViewerWindow->getWindow()->decBusyCount(); -} - -//If we get back an error (not found, etc...), handle it here -void LLInventoryModel::fetchInventoryResponder::httpFailure(void) -{ - LL_INFOS() << "fetchInventory::error " - << mStatus << ": " << mReason << LL_ENDL; - gInventory.notifyObservers(); } bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const { if(folder_id.isNull()) { - LL_WARNS() << "Calling fetch descendents on NULL folder id!" << LL_ENDL; + LL_WARNS(LOG_INV) << "Calling fetch descendents on NULL folder id!" << LL_ENDL; return false; } LLViewerInventoryCategory* cat = getCategory(folder_id); if(!cat) { - LL_WARNS() << "Asked to fetch descendents of non-existent folder: " - << folder_id << LL_ENDL; + LL_WARNS(LOG_INV) << "Asked to fetch descendents of non-existent folder: " + << folder_id << LL_ENDL; return false; } //S32 known_descendents = 0; @@ -1556,11 +1671,11 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id); //if(categories) //{ - // known_descendents += categories->count(); + // known_descendents += categories->size(); //} //if(items) //{ - // known_descendents += items->count(); + // known_descendents += items->size(); //} return cat->fetch(); } @@ -1569,8 +1684,8 @@ void LLInventoryModel::cache( const LLUUID& parent_folder_id, const LLUUID& agent_id) { - LL_DEBUGS() << "Caching " << parent_folder_id << " for " << agent_id - << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id + << LL_ENDL; LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id); if(!root_cat) return; cat_array_t categories; @@ -1595,23 +1710,23 @@ void LLInventoryModel::cache( gzip_filename.append(".gz"); if(gzip_file(inventory_filename, gzip_filename)) { - LL_DEBUGS() << "Successfully compressed " << inventory_filename << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Successfully compressed " << inventory_filename << LL_ENDL; LLFile::remove(inventory_filename); } else { - LL_WARNS() << "Unable to compress " << inventory_filename << LL_ENDL; + LL_WARNS(LOG_INV) << "Unable to compress " << inventory_filename << LL_ENDL; } } void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) { - //LL_INFOS() << "LLInventoryModel::addCategory()" << LL_ENDL; + //LL_INFOS(LOG_INV) << "LLInventoryModel::addCategory()" << LL_ENDL; if(category) { // We aren't displaying the Meshes folder - if (category->getPreferredType() == LLFolderType::FT_MESH) + if (category->mPreferredType == LLFolderType::FT_MESH) { return; } @@ -1625,6 +1740,47 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) } } +bool LLInventoryModel::hasBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) const +{ + std::pair range; + range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) + { + if (it->second == link_id) + { + return true; + } + } + return false; +} + +void LLInventoryModel::addBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) +{ + if (!hasBacklinkInfo(link_id, target_id)) + { + mBacklinkMMap.insert(std::make_pair(target_id, link_id)); + } +} + +void LLInventoryModel::removeBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) +{ + std::pair range; + range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::iterator it = range.first; it != range.second; ) + { + if (it->second == link_id) + { + backlink_mmap_t::iterator delete_it = it; // iterator will be invalidated by erase. + ++it; + mBacklinkMMap.erase(delete_it); + } + else + { + ++it; + } + } +} + void LLInventoryModel::addItem(LLViewerInventoryItem* item) { llassert(item); @@ -1636,7 +1792,9 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) if ((item->getType() == LLAssetType::AT_NONE) || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) { - LL_WARNS() << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL; + LL_WARNS(LOG_INV) << "Got bad asset type for item [ name: " << item->getName() + << " type: " << item->getType() + << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL; return; } @@ -1644,9 +1802,17 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // The item will show up as a broken link. if (item->getIsBrokenLink()) { - LL_INFOS() << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + } + if (item->getIsLinkType()) + { + // Add back-link from linked-to UUID. + const LLUUID& link_id = item->getUUID(); + const LLUUID& target_id = item->getLinkedUUID(); + addBacklinkInfo(link_id, target_id); } - mItemMap[item->getUUID()] = item; } } @@ -1665,6 +1831,7 @@ void LLInventoryModel::empty() mParentChildItemTree.end(), DeletePairedPointer()); mParentChildItemTree.clear(); + mBacklinkMMap.clear(); // forget all backlink information. mCategoryMap.clear(); // remove all references (should delete entries) mItemMap.clear(); // remove all references (should delete entries) mLastItem = NULL; @@ -1676,42 +1843,39 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const LLViewerInventoryCategory* cat = getCategory(update.mCategoryID); if(cat) { - bool accounted = false; S32 version = cat->getVersion(); if(version != LLViewerInventoryCategory::VERSION_UNKNOWN) { S32 descendents_server = cat->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - getDirectDescendentsOf(update.mCategoryID, cats, items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->size() + items->size(); - } + S32 descendents_actual = cat->getViewerDescendentCount(); if(descendents_server == descendents_actual) { - accounted = true; descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - LL_DEBUGS() << "accounted: '" << cat->getName() << "' " - << version << " with " << descendents_actual - << " descendents." << LL_ENDL; + LL_DEBUGS(LOG_INV) << "accounted: '" << cat->getName() << "' " + << version << " with " << descendents_actual + << " descendents." << LL_ENDL; + } + else + { + // Error condition, this means that the category did not register that + // it got new descendents (perhaps because it is still being loaded) + // which means its descendent count will be wrong. + LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version:" + << version << " due to mismatched descendent count: server == " + << descendents_server << ", viewer == " << descendents_actual << LL_ENDL; } } - if(!accounted) + else { - // Error condition, this means that the category did not register that - // it got new descendents (perhaps because it is still being loaded) - // which means its descendent count will be wrong. - LL_WARNS() << "Accounting failed for '" << cat->getName() << "' version:" - << version << LL_ENDL; + LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version: unknown (" + << version << ")" << LL_ENDL; } } else { - LL_WARNS() << "No category found for update " << update.mCategoryID << LL_ENDL; + LL_WARNS(LOG_INV) << "No category found for update " << update.mCategoryID << LL_ENDL; } } @@ -1740,47 +1904,6 @@ void LLInventoryModel::accountForUpdate( } } - -/* -void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id) -{ - LLViewerInventoryCategory* cat = getCategory(category_id); - if(cat) - { - S32 version = cat->getVersion(); - if(LLViewerInventoryCategory::VERSION_UNKNOWN != version) - { - cat->setVersion(version + 1); - LL_INFOS() << "IncrementVersion: " << cat->getName() << " " - << cat->getVersion() << LL_ENDL; - } - else - { - LL_INFOS() << "Attempt to increment version when unknown: " - << category_id << LL_ENDL; - } - } - else - { - LL_INFOS() << "Attempt to increment category: " << category_id << LL_ENDL; - } -} -void LLInventoryModel::incrementCategorySetVersion( - const std::set& categories) -{ - if(!categories.empty()) - { - std::set::const_iterator it = categories.begin(); - std::set::const_iterator end = categories.end(); - for(; it != end; ++it) - { - incrementCategoryVersion(*it); - } - } -} -*/ - - LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren( const LLUUID& cat_id) const { @@ -1821,20 +1944,12 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN)) { S32 descendents_server = cat->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - getDirectDescendentsOf(cat_id, cats, items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->size() + items->size(); - } + S32 descendents_actual = cat->getViewerDescendentCount(); if(descendents_server == descendents_actual) { return true; } } - return false; } @@ -1842,7 +1957,7 @@ bool LLInventoryModel::loadSkeleton( const LLSD& options, const LLUUID& owner_id) { - LL_DEBUGS() << "importing inventory skeleton for " << owner_id << LL_ENDL; + LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL; typedef std::set, InventoryIDPtrLess> cat_set_t; cat_set_t temp_cats; @@ -1879,7 +1994,7 @@ bool LLInventoryModel::loadSkeleton( } else { - LL_WARNS() << "Unable to import near " << name.asString() << LL_ENDL; + LL_WARNS(LOG_INV) << "Unable to import near " << name.asString() << LL_ENDL; rv = false; } } @@ -1916,11 +2031,11 @@ bool LLInventoryModel::loadSkeleton( } else { - LL_INFOS() << "Unable to gunzip " << gzip_filename << LL_ENDL; + LL_INFOS(LOG_INV) << "Unable to gunzip " << gzip_filename << LL_ENDL; } } bool is_cache_obsolete = false; - if (loadFromFile(inventory_filename, categories, items, is_cache_obsolete)) + if(loadFromFile(inventory_filename, categories, items, is_cache_obsolete)) { // We were able to find a cache of files. So, use what we // found to generate a set of categories we should add. We @@ -1929,7 +2044,7 @@ bool LLInventoryModel::loadSkeleton( S32 count = categories.size(); cat_set_t::iterator not_cached = temp_cats.end(); std::set cached_ids; - for (S32 i = 0; i < count; ++i) + for(S32 i = 0; i < count; ++i) { LLViewerInventoryCategory* cat = categories[i]; cat_set_t::iterator cit = temp_cats.find(cat); @@ -1997,10 +2112,10 @@ bool LLInventoryModel::loadSkeleton( if (item->getIsBrokenLink()) { //bad_link_count++; - LL_DEBUGS() << "Attempted to add cached link item without baseobj present ( name: " - << item->getName() << " itemID: " << item->getUUID() - << " assetID: " << item->getAssetUUID() - << " ). Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Attempted to add cached link item without baseobj present ( name: " + << item->getName() << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL; possible_broken_links.push_back(item); continue; } @@ -2039,11 +2154,11 @@ bool LLInventoryModel::loadSkeleton( } } - LL_INFOS() << "Attempted to add " << bad_link_count - << " cached link items without baseobj present. " - << good_link_count << " link items were successfully added. " - << recovered_link_count << " links added in recovery. " - << "The corresponding categories were invalidated." << LL_ENDL; + LL_INFOS(LOG_INV) << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << good_link_count << " link items were successfully added. " + << recovered_link_count << " links added in recovery. " + << "The corresponding categories were invalidated." << LL_ENDL; } } @@ -2067,8 +2182,9 @@ bool LLInventoryModel::loadSkeleton( { LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); cat->setVersion(NO_VERSION); - LL_INFOS() << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL; } + LL_INFOS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL; // At this point, we need to set the known descendents for each // category which successfully cached so that we do not @@ -2097,18 +2213,18 @@ bool LLInventoryModel::loadSkeleton( // clean up the gunzipped file. LLFile::remove(inventory_filename); } - if (is_cache_obsolete) + if(is_cache_obsolete) { // If out of date, remove the gzipped file too. - LL_WARNS() << "Inv cache out of date, removing" << LL_ENDL; + LL_WARNS(LOG_INV) << "Inv cache out of date, removing" << LL_ENDL; LLFile::remove(gzip_filename); } categories.clear(); // will unref and delete entries } - LL_INFOS() << "Successfully loaded " << cached_category_count - << " categories and " << cached_item_count << " items from cache." - << LL_ENDL; + LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count + << " categories and " << cached_item_count << " items from cache." + << LL_ENDL; return rv; } @@ -2118,7 +2234,7 @@ bool LLInventoryModel::loadSkeleton( // should be sufficient for our needs. void LLInventoryModel::buildParentChildMap() { - LL_INFOS() << "LLInventoryModel::buildParentChildMap()" << LL_ENDL; + LL_INFOS(LOG_INV) << "LLInventoryModel::buildParentChildMap()" << LL_ENDL; // *NOTE: I am skipping the logic around folder version // synchronization here because it seems if a folder is lost, we @@ -2166,11 +2282,16 @@ void LLInventoryModel::buildParentChildMap() S32 count = cats.size(); S32 i; S32 lost = 0; + cat_array_t lost_cats; for(i = 0; i < count; ++i) { LLViewerInventoryCategory* cat = cats.at(i); catsp = getUnlockedCatArray(cat->getParentUUID()); - if(catsp) + if(catsp && + // Only the two root folders should be children of null. + // Others should go to lost & found. + (cat->getParentUUID().notNull() || + cat->getPreferredType() == LLFolderType::FT_ROOT_INVENTORY )) { catsp->push_back(cat); } @@ -2182,12 +2303,26 @@ void LLInventoryModel::buildParentChildMap() // implement it, we would need a set or map of uuid pairs // which would be (folder_id, new_parent_id) to be sent up // to the server. - LL_INFOS() << "Lost categroy: " << cat->getUUID() << " - " - << cat->getName() << " with parent:" << cat->getParentUUID() << LL_ENDL; + LL_INFOS(LOG_INV) << "Lost category: " << cat->getUUID() << " - " + << cat->getName() << LL_ENDL; ++lost; - // plop it into the lost & found. - LLFolderType::EType pref = cat->getPreferredType(); - if(LLFolderType::FT_NONE == pref) + lost_cats.push_back(cat); + } + } + if(lost) + { + LL_WARNS(LOG_INV) << "Found " << lost << " lost categories." << LL_ENDL; + } + + // Do moves in a separate pass to make sure we've properly filed + // the FT_LOST_AND_FOUND category before we try to find its UUID. + for(i = 0; igetPreferredType(); + if(LLFolderType::FT_NONE == pref) { cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); } @@ -2204,18 +2339,13 @@ void LLInventoryModel::buildParentChildMap() cat->updateServer(TRUE); catsp = getUnlockedCatArray(cat->getParentUUID()); if(catsp) - { - catsp->push_back(cat); - } - else - { - LL_WARNS() << "Lost and found Not there!!" << LL_ENDL; - } + { + catsp->push_back(cat); + } + else + { + LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL; } - } - if(lost) - { - LL_WARNS() << "Found " << lost << " lost categories." << LL_ENDL; } const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); @@ -2249,8 +2379,8 @@ void LLInventoryModel::buildParentChildMap() } else { - LL_INFOS() << "Lost item: " << item->getUUID() << " - " - << item->getName() << LL_ENDL; + LL_INFOS(LOG_INV) << "Lost item: " << item->getUUID() << " - " + << item->getName() << LL_ENDL; ++lost; // plop it into the lost & found. // @@ -2266,13 +2396,13 @@ void LLInventoryModel::buildParentChildMap() } else { - LL_WARNS() << "Lost and found Not there!!" << LL_ENDL; + LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL; } } } if(lost) { - LL_WARNS() << "Found " << lost << " lost items." << LL_ENDL; + LL_WARNS(LOG_INV) << "Found " << lost << " lost items." << LL_ENDL; LLMessageSystem* msg = gMessageSystem; BOOL start_new_message = TRUE; const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); @@ -2341,13 +2471,25 @@ void LLInventoryModel::buildParentChildMap() // The inv tree is built. mIsAgentInvUsable = true; AIEvent::trigger(AIEvent::LLInventoryModel_mIsAgentInvUsable_true); - LL_INFOS() << "Inventory initialized, notifying observers" << LL_ENDL; - addChangedMask(LLInventoryObserver::ALL, LLUUID::null); - notifyObservers(); + // notifyObservers() has been moved to + // llstartup/idle_startup() after this func completes. + // Allows some system categories to be created before + // observers start firing. } } - LL_INFOS() << " finished buildParentChildMap " << LL_ENDL; - // dumpInventory(); // enable this if debugging inventory or appearance issues OGPX + + if (!gInventory.validate()) + { + LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL; + } +} + +void LLInventoryModel::createCommonSystemCategories() +{ + gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true); } struct LLUUIDAndName @@ -2390,14 +2532,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, { if(filename.empty()) { - LL_ERRS() << "Filename is Null!" << LL_ENDL; + LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; return false; } - LL_INFOS() << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL; + LL_INFOS(LOG_INV) << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL; LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ if(!file) { - LL_INFOS() << "unable to load inventory from: " << filename << LL_ENDL; + LL_INFOS(LOG_INV) << "unable to load inventory from: " << filename << LL_ENDL; return false; } // *NOTE: This buffer size is hard coded into scanf() below. @@ -2436,7 +2578,7 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } else { - LL_WARNS() << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL; + LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL; //delete inv_cat; // automatic when inv_cat is reassigned or destroyed } } @@ -2454,8 +2596,8 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, if(inv_item->getUUID().isNull()) { //delete inv_item; // automatic when inv_cat is reassigned or destroyed - LL_WARNS() << "Ignoring inventory with null item id: " - << inv_item->getName() << LL_ENDL; + LL_WARNS(LOG_INV) << "Ignoring inventory with null item id: " + << inv_item->getName() << LL_ENDL; } else @@ -2465,14 +2607,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } else { - LL_WARNS() << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL; + LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL; //delete inv_item; // automatic when inv_cat is reassigned or destroyed } } else { - LL_WARNS() << "Unknown token in inventory file '" << keyword << "'" - << LL_ENDL; + LL_WARNS(LOG_INV) << "Unknown token in inventory file '" << keyword << "'" + << LL_ENDL; } } fclose(file); @@ -2488,18 +2630,19 @@ bool LLInventoryModel::saveToFile(const std::string& filename, { if(filename.empty()) { - LL_ERRS() << "Filename is Null!" << LL_ENDL; + LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; return false; } - LL_INFOS() << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL; + LL_INFOS(LOG_INV) << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL; LLFILE* file = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ if(!file) { - LL_WARNS() << "unable to save inventory to: " << filename << LL_ENDL; + LL_WARNS(LOG_INV) << "unable to save inventory to: " << filename << LL_ENDL; return false; } fprintf(file, "\tinv_cache_version\t%d\n", sCurrentInvCacheVersion); + S32 count = categories.size(); S32 i; for(i = 0; i < count; ++i) @@ -2571,7 +2714,7 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg) void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**) { // do accounting and highlight new items if they arrive - if (gInventory.messageUpdateCore(msg, true)) + if (gInventory.messageUpdateCore(msg, true, LLInventoryObserver::UPDATE_CREATE)) { U32 callback_id; LLUUID item_id; @@ -2591,7 +2734,7 @@ void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**) } -bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) +bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32 mask) { //make sure our added inventory observer is active start_new_inventory_observer(); @@ -2600,8 +2743,8 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a inventory update for the wrong agent: " << agent_id - << LL_ENDL; + LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id + << LL_ENDL; return false; } item_array_t items; @@ -2613,8 +2756,8 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) { LLPointer titem = new LLViewerInventoryItem; titem->unpackMessage(msg, _PREHASH_InventoryData, i); - LL_DEBUGS() << "LLInventoryModel::messageUpdateCore() item id:" - << titem->getUUID() << LL_ENDL; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::messageUpdateCore() item id: " + << titem->getUUID() << LL_ENDL; items.push_back(titem); // examine update for changes. LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); @@ -2645,10 +2788,14 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) } U32 changes = 0x0; + if (account) + { + mask |= LLInventoryObserver::CREATE; + } //as above, this loop never seems to loop more than once per call for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) { - changes |= gInventory.updateItem(*it); + changes |= gInventory.updateItem(*it, mask); } gInventory.notifyObservers(); gViewerWindow->getWindow()->decBusyCount(); @@ -2661,17 +2808,17 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg { LLUUID item_id; S32 count = msg->getNumberOfBlocksFast(msg_label); - LL_DEBUGS() << "Message has " << count << " item blocks" << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Message has " << count << " item blocks" << LL_ENDL; uuid_vec_t item_ids; update_map_t update; for(S32 i = 0; i < count; ++i) { msg->getUUIDFast(msg_label, _PREHASH_ItemID, item_id, i); - LL_DEBUGS() << "Checking for item-to-be-removed " << item_id << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Checking for item-to-be-removed " << item_id << LL_ENDL; LLViewerInventoryItem* itemp = gInventory.getItem(item_id); if(itemp) { - LL_DEBUGS() << "Item will be removed " << item_id << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Item will be removed " << item_id << LL_ENDL; // we only bother with the delete and account if we found // the item - this is usually a back-up for permissions, // so frequently the item will already be gone. @@ -2682,7 +2829,7 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg gInventory.accountForUpdate(update); for(uuid_vec_t::iterator it = item_ids.begin(); it != item_ids.end(); ++it) { - LL_DEBUGS() << "Calling deleteObject " << *it << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Calling deleteObject " << *it << LL_ENDL; gInventory.deleteObject(*it); } } @@ -2690,13 +2837,13 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg // static void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) { - LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL; LLUUID agent_id, item_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a RemoveInventoryItem for the wrong agent." - << LL_ENDL; + LL_WARNS(LOG_INV) << "Got a RemoveInventoryItem for the wrong agent." + << LL_ENDL; return; } LLInventoryModel::removeInventoryItem(agent_id, msg, _PREHASH_InventoryData); @@ -2707,14 +2854,14 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, void**) { - LL_DEBUGS() << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; LLUUID agent_id, folder_id, parent_id; //char name[DB_INV_ITEM_NAME_BUF_SIZE]; msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got an UpdateInventoryFolder for the wrong agent." - << LL_ENDL; + LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent." + << LL_ENDL; return; } LLPointer lastfolder; // hack @@ -2798,7 +2945,7 @@ void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a RemoveInventoryFolder for the wrong agent." + LL_WARNS() << "Got a RemoveInventoryFolder for the wrong agent." << LL_ENDL; return; } @@ -2816,7 +2963,7 @@ void LLInventoryModel::processRemoveInventoryObjects(LLMessageSystem* msg, msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a RemoveInventoryObjects for the wrong agent." + LL_WARNS() << "Got a RemoveInventoryObjects for the wrong agent." << LL_ENDL; return; } @@ -2833,7 +2980,7 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg, msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a SaveAssetIntoInventory message for the wrong agent." + LL_WARNS() << "Got a SaveAssetIntoInventory message for the wrong agent." << LL_ENDL; return; } @@ -2888,13 +3035,13 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL; + LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL; return; } LLUUID tid; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid); #ifndef LL_RELEASE_FOR_DOWNLOAD - LL_INFOS() << "Bulk inventory: " << tid << LL_ENDL; + LL_DEBUGS("Inventory") << "Bulk inventory: " << tid << LL_ENDL; #endif update_map_t update; @@ -2906,9 +3053,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLPointer tfolder = new LLViewerInventoryCategory(gAgent.getID()); tfolder->unpackMessage(msg, _PREHASH_FolderData, i); - //LL_INFOS() << "unpacked folder '" << tfolder->getName() << "' (" - // << tfolder->getUUID() << ") in " << tfolder->getParentUUID() - // << LL_ENDL; + LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" + << tfolder->getUUID() << ") in " << tfolder->getParentUUID() + << LL_ENDL; if(tfolder->getUUID().notNull()) { folders.push_back(tfolder); @@ -2962,8 +3109,8 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLPointer titem = new LLViewerInventoryItem; titem->unpackMessage(msg, _PREHASH_ItemData, i); - //LL_INFOS() << "unpacked item '" << titem->getName() << "' in " - // << titem->getParentUUID() << LL_ENDL; + LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in " + << titem->getParentUUID() << LL_ENDL; U32 callback_id; msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id); if(titem->getUUID().notNull() ) // && callback_id.notNull() ) @@ -3069,7 +3216,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." << LL_ENDL; + LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." << LL_ENDL; return; } LLUUID parent_id; @@ -3098,7 +3245,8 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) // If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added. if (gInventory.getItem(titem->getUUID())) { - LL_DEBUGS() << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << LL_ENDL; + LL_DEBUGS("Inventory") << "Skipping prefetched item [ Name: " << titem->getName() + << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << LL_ENDL; continue; } gInventory.updateItem(titem); @@ -3125,7 +3273,7 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got a MoveInventoryItem message for the wrong agent." + LL_WARNS() << "Got a MoveInventoryItem message for the wrong agent." << LL_ENDL; return; } @@ -3184,8 +3332,7 @@ bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const L if (option == 0) // YES { const LLUUID folder_id = findCategoryUUIDForType(preferred_type); - purgeDescendentsOf(folder_id); - notifyObservers(); + purge_descendents_of(folder_id, NULL); } return false; } @@ -3200,8 +3347,7 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderT else { const LLUUID folder_id = findCategoryUUIDForType(preferred_type); - purgeDescendentsOf(folder_id); - notifyObservers(); + purge_descendents_of(folder_id, NULL); } } @@ -3320,7 +3466,7 @@ BOOL LLInventoryModel::getIsFirstTimeInViewer2() // Do not call this before parentchild map is built. if (!gInventory.mIsAgentInvUsable) { - LL_WARNS() << "Parent Child Map not yet built; guessing as first time in viewer2." << LL_ENDL; + LL_WARNS() << "Parent Child Map not yet built; guessing as first time in viewer2." << LL_ENDL; return TRUE; } @@ -3488,12 +3634,311 @@ void LLInventoryModel::dumpInventory() const LL_INFOS() << "\n**********************\nEnd Inventory Dump" << LL_ENDL; } +// Do various integrity checks on model, logging issues found and +// returning an overall good/bad flag. +bool LLInventoryModel::validate() const +{ + bool valid = true; + + if (getRootFolderID().isNull()) + { + LL_WARNS() << "no root folder id" << LL_ENDL; + valid = false; + } + if (getLibraryRootFolderID().isNull()) + { + LL_WARNS() << "no root folder id" << LL_ENDL; + valid = false; + } + + if (mCategoryMap.size() + 1 != mParentChildCategoryTree.size()) + { + // ParentChild should be one larger because of the special entry for null uuid. + LL_INFOS() << "unexpected sizes: cat map size " << mCategoryMap.size() + << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; + valid = false; + } + S32 cat_lock = 0; + S32 item_lock = 0; + S32 desc_unknown_count = 0; + S32 version_unknown_count = 0; + for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) + { + const LLUUID& cat_id = cit->first; + const LLViewerInventoryCategory *cat = cit->second; + if (!cat) + { + LL_WARNS() << "invalid cat" << LL_ENDL; + valid = false; + continue; + } + if (cat_id != cat->getUUID()) + { + LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; + valid = false; + } + + if (cat->getParentUUID().isNull()) + { + if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID()) + { + LL_WARNS() << "cat " << cat_id << " has no parent, but is not root (" + << getRootFolderID() << ") or library root (" + << getLibraryRootFolderID() << ")" << LL_ENDL; + } + } + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(cat_id,cats,items); + if (!cats || !items) + { + LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL; + valid = false; + continue; + } + if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) + { + desc_unknown_count++; + } + else if (cats->size() + items->size() != cat->getDescendentCount()) + { + LL_WARNS() << "invalid desc count for " << cat_id << " name [" << cat->getName() + << "] parent " << cat->getParentUUID() + << " cached " << cat->getDescendentCount() + << " expected " << cats->size() << "+" << items->size() + << "=" << cats->size() +items->size() << LL_ENDL; + valid = false; + } + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + version_unknown_count++; + } + if (mCategoryLock.count(cat_id)) + { + cat_lock++; + } + if (mItemLock.count(cat_id)) + { + item_lock++; + } + for (S32 i = 0; isize(); i++) + { + LLViewerInventoryItem *item = items->at(i); + + if (!item) + { + LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL; + valid = false; + continue; + } + + const LLUUID& item_id = item->getUUID(); + + if (item->getParentUUID() != cat_id) + { + LL_WARNS() << "wrong parent for " << item_id << " found " + << item->getParentUUID() << " expected " << cat_id + << LL_ENDL; + valid = false; + } + + + // Entries in items and mItemMap should correspond. + item_map_t::const_iterator it = mItemMap.find(item_id); + if (it == mItemMap.end()) + { + LL_WARNS() << "item " << item_id << " found as child of " + << cat_id << " but not in top level mItemMap" << LL_ENDL; + valid = false; + } + else + { + LLViewerInventoryItem *top_item = it->second; + if (top_item != item) + { + LL_WARNS() << "item mismatch, item_id " << item_id + << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL; + } + } + + // Topmost ancestor should be root or library. + LLUUID topmost_ancestor_id; + bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id); + if (!found) + { + LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL; + valid = false; + } + else + { + if (topmost_ancestor_id != getRootFolderID() && + topmost_ancestor_id != getLibraryRootFolderID()) + { + LL_WARNS() << "unrecognized top level ancestor for " << item_id + << " got " << topmost_ancestor_id + << " expected " << getRootFolderID() + << " or " << getLibraryRootFolderID() << LL_ENDL; + valid = false; + } + } + } + + // Does this category appear as a child of its supposed parent? + const LLUUID& parent_id = cat->getParentUUID(); + if (!parent_id.isNull()) + { + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(parent_id,cats,items); + if (!cats) + { + LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; + valid = false; + } + else + { + bool found = false; + for (S32 i = 0; isize(); i++) + { + LLViewerInventoryCategory *kid_cat = cats->at(i); + if (kid_cat == cat) + { + found = true; + break; + } + } + if (!found) + { + LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; + } + } + } + } + + for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) + { + const LLUUID& item_id = iit->first; + LLViewerInventoryItem *item = iit->second; + if (item->getUUID() != item_id) + { + LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; + valid = false; + } + + const LLUUID& parent_id = item->getParentUUID(); + if (parent_id.isNull()) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; + } + else + { + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(parent_id,cats,items); + if (!items) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() + << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL; + } + else + { + bool found = false; + for (S32 i=0; isize(); ++i) + { + if (items->at(i) == item) + { + found = true; + break; + } + } + if (!found) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() + << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; + } + } + + } + // Link checking + if (item->getIsLinkType()) + { + const LLUUID& link_id = item->getUUID(); + const LLUUID& target_id = item->getLinkedUUID(); + LLViewerInventoryItem *target_item = getItem(target_id); + LLViewerInventoryCategory *target_cat = getCategory(target_id); + // Linked-to UUID should have back reference to this link. + if (!hasBacklinkInfo(link_id, target_id)) + { + LL_WARNS() << "link " << item->getUUID() << " type " << item->getActualType() + << " missing backlink info at target_id " << target_id + << LL_ENDL; + } + // Links should have referents. + if (item->getActualType() == LLAssetType::AT_LINK && !target_item) + { + LL_WARNS() << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat) + { + LL_WARNS() << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + if (target_item && target_item->getIsLinkType()) + { + LL_WARNS() << "link " << item->getName() << " references a link item " + << target_item->getName() << " " << target_item->getUUID() << LL_ENDL; + } + + // Links should not have backlinks. + std::pair range = mBacklinkMMap.equal_range(link_id); + if (range.first != range.second) + { + LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL; + } + } + else + { + // Check the backlinks of a non-link item. + const LLUUID& target_id = item->getUUID(); + std::pair range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) + { + const LLUUID& link_id = it->second; + LLViewerInventoryItem *link_item = getItem(link_id); + if (!link_item || !link_item->getIsLinkType()) + { + LL_WARNS() << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL; + } + } + } + } + + if (cat_lock > 0 || item_lock > 0) + { + LL_INFOS() << "Found locks on some categories: sub-cat arrays " + << cat_lock << ", item arrays " << item_lock << LL_ENDL; + } + if (desc_unknown_count != 0) + { + LL_INFOS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL; + } + if (version_unknown_count != 0) + { + LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL; + } + + LL_INFOS() << "Validate done, valid = " << (U32) valid << LL_ENDL; + + return valid; +} + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- -/* +#if 0 BOOL decompress_file(const char* src_filename, const char* dst_filename) { BOOL rv = FALSE; @@ -3532,4 +3977,80 @@ BOOL decompress_file(const char* src_filename, const char* dst_filename) if(dst != NULL) fclose(dst); return rv; } -*/ +#endif + +// If we get back a normal response, handle it here +void LLInventoryModel::FetchItemHttpHandler::httpSuccess(void) +{ + start_new_inventory_observer(); + +#if 0 + LLUUID agent_id; + agent_id = content["agent_id"].asUUID(); + if (agent_id != gAgent.getID()) + { + LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id + << LL_ENDL; + return; + } +#endif + + LLInventoryModel::item_array_t items; + LLInventoryModel::update_map_t update; + LLUUID folder_id; + LLSD content_items(mContent["items"]); + const S32 count(content_items.size()); + + // Does this loop ever execute more than once? + for (S32 i(0); i < count; ++i) + { + LLPointer titem = new LLViewerInventoryItem; + titem->unpackMessage(content_items[i]); + + LL_DEBUGS(LOG_INV) << "ItemHttpHandler::httpSuccess item id: " + << titem->getUUID() << LL_ENDL; + items.push_back(titem); + + // examine update for changes. + LLViewerInventoryItem * itemp(gInventory.getItem(titem->getUUID())); + + if (itemp) + { + if(titem->getParentUUID() == itemp->getParentUUID()) + { + update[titem->getParentUUID()]; + } + else + { + ++update[titem->getParentUUID()]; + --update[itemp->getParentUUID()]; + } + } + else + { + ++update[titem->getParentUUID()]; + } + if (folder_id.isNull()) + { + folder_id = titem->getParentUUID(); + } + } + + // as above, this loop never seems to loop more than once per call + U32 changes(0U); + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) + { + changes |= gInventory.updateItem(*it); + } + // *HUH: Have computed 'changes', nothing uses it. + + gInventory.notifyObservers(); + gViewerWindow->getWindow()->decBusyCount(); +} +//If we get back an error (not found, etc...), handle it here +void LLInventoryModel::FetchItemHttpHandler::httpFailure(void) +{ + LL_INFOS() << "FetchItemHttpHandler::error " + << mStatus << ": " << mReason << LL_ENDL; + gInventory.notifyObservers(); +} diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 74808782a..da2937d00 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -27,32 +27,28 @@ #ifndef LL_LLINVENTORYMODEL_H #define LL_LLINVENTORYMODEL_H +#include +#include +#include +#include + #include "llassettype.h" #include "llfoldertype.h" #include "llframetimer.h" #include "llhttpclient.h" #include "lluuid.h" #include "llpermissionsflags.h" +#include "llviewerinventory.h" #include "llstring.h" - #include "llmd5.h" -#include -#include -#include -#include - class AIHTTPTimeoutPolicy; -extern AIHTTPTimeoutPolicy fetchInventoryResponder_timeout; +extern AIHTTPTimeoutPolicy FetchItemHttpHandler_timeout; class LLInventoryObserver; class LLInventoryObject; class LLInventoryItem; class LLInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; class LLMessageSystem; class LLInventoryCollectFunctor; @@ -82,15 +78,17 @@ public: typedef std::vector > item_array_t; typedef std::set changed_items_t; - class fetchInventoryResponder : public LLHTTPClient::ResponderWithResult + class FetchItemHttpHandler : public LLHTTPClient::ResponderWithResult { public: - fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; + LOG_CLASS(FetchItemHttpHandler); + + FetchItemHttpHandler(const LLSD& request_sd) : mRequestSD(request_sd) {}; /*virtual*/ void httpSuccess(void); /*virtual*/ void httpFailure(void); /*virtual*/ AICapabilityType capability_type(void) const { return cap_inventory; } - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return fetchInventoryResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "fetchInventoryResponder"; } + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return FetchItemHttpHandler_timeout; } + /*virtual*/ char const* getName(void) const { return "FetchItemHttpHandler"; } protected: LLSD mRequestSD; }; @@ -146,6 +144,8 @@ public: // during authentication. Returns true if everything parsed. bool loadSkeleton(const LLSD& options, const LLUUID& owner_id); void buildParentChildMap(); // brute force method to rebuild the entire parent-child relations + void createCommonSystemCategories(); + // Call on logout to save a terse representation. void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id); private: @@ -164,6 +164,15 @@ private: parent_cat_map_t mParentChildCategoryTree; parent_item_map_t mParentChildItemTree; + // Track links to items and categories. We do not store item or + // category pointers here, because broken links are also supported. + typedef std::multimap backlink_mmap_t; + backlink_mmap_t mBacklinkMMap; // key = target_id: ID of item, values = link_ids: IDs of item or folder links referencing it. + // For internal use only + bool hasBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) const; + void addBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id); + void removeBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id); + //-------------------------------------------------------------------- // Login //-------------------------------------------------------------------- @@ -215,6 +224,9 @@ public: EXCLUDE_TRASH = FALSE, INCLUDE_TRASH = TRUE }; + // Simpler existence test if matches don't actually need to be collected. + bool hasMatchingDirectDescendent(const LLUUID& cat_id, + LLInventoryCollectFunctor& filter, bool follow_folder_links = false); void collectDescendents(const LLUUID& id, cat_array_t& categories, item_array_t& items, @@ -228,25 +240,35 @@ public: // Collect all items in inventory that are linked to item_id. // Assumes item_id is itself not a linked item. - item_array_t collectLinkedItems(const LLUUID& item_id, + item_array_t collectLinksTo(const LLUUID& item_id, const LLUUID& start_folder_id = LLUUID::null); // Check if one object has a parent chain up to the category specified by UUID. BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id, const BOOL break_on_recursion=FALSE) const; + // Follow parent chain to the top. + bool getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const; + //-------------------------------------------------------------------- // Find //-------------------------------------------------------------------- public: + const LLUUID findCategoryUUIDForTypeInRoot( + LLFolderType::EType preferred_type, + bool create_folder, + const LLUUID& root_id); + // 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, - bool find_in_library = false); + bool create_folder = true); + // will search in the user's library folder instead of "My Inventory" + const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder = true); // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; @@ -308,7 +330,7 @@ public: // NOTE: In usage, you will want to perform cache accounting // operations in LLInventoryModel::accountForUpdate() or // LLViewerInventoryItem::updateServer() before calling this method. - U32 updateItem(const LLViewerInventoryItem* item); + U32 updateItem(const LLViewerInventoryItem* item, U32 mask = 0); // Change an existing item with the matching id or add // the category. No notifcation will be sent to observers. This @@ -317,7 +339,7 @@ public: // NOTE: In usage, you will want to perform cache accounting // operations in accountForUpdate() or LLViewerInventoryCategory:: // updateServer() before calling this method. - void updateCategory(const LLViewerInventoryCategory* cat); + void updateCategory(const LLViewerInventoryCategory* cat, U32 mask = 0); // Move the specified object id to the specified category and // update the internal structures. No cache accounting, @@ -338,11 +360,27 @@ public: // Delete //-------------------------------------------------------------------- public: + // Update model after an item is confirmed as removed from + // server. Works for categories or items. + void onObjectDeletedFromServer(const LLUUID& item_id, + bool fix_broken_links = true, + bool update_parent_version = true, + bool do_notify_observers = true); + + // Update model after all descendents removed from server. + void onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links = true); + + // Update model after an existing item gets updated on server. + void onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version); + + // Update model after an existing category gets updated on server. + void onCategoryUpdated(const LLUUID& cat_id, const LLSD& updates); + // Delete a particular inventory object by ID. Will purge one // object from the internal data structures, maintaining a // consistent internal state. No cache accounting, observer // notification, or server update is performed. - void deleteObject(const LLUUID& id); + void deleteObject(const LLUUID& id, bool fix_broken_links = true, bool do_notify_observers = true); /// move Item item_id to Trash void removeItem(const LLUUID& item_id); /// move Category category_id to Trash @@ -350,17 +388,6 @@ public: /// removeItem() or removeCategory(), whichever is appropriate void removeObject(const LLUUID& object_id); - // Delete a particular inventory object by ID, and delete it from - // the server. Also updates linked items. - void purgeObject(const LLUUID& id); - - // Collects and purges the descendants of the id - // provided. If the category is not found, no action is - // taken. This method goes through the long winded process of - // removing server representation of folders and items while doing - // cache accounting in a fairly efficient manner. This method does - // not notify observers (though maybe it should...) - void purgeDescendentsOf(const LLUUID& id); protected: void updateLinkedObjectsFromPurge(const LLUUID& baseobj_id); @@ -398,9 +425,8 @@ public: LLUUID createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name, - void (*callback)(const LLSD&, void*) = NULL, - void* user_data = NULL); -public: + boost::optional callback = boost::optional()); +protected: // Internal methods that add inventory and make sure that all of // the internal data structures are consistent. These methods // should be passed pointers of newly created objects, and the @@ -476,6 +502,7 @@ public: // inventory. The next notify will include that notification. void addChangedMask(U32 mask, const LLUUID& referent); const changed_items_t& getChangedIDs() const { return mChangedItemIDs; } + const changed_items_t& getAddedIDs() const { return mAddedItemIDs; } protected: // Updates all linked items pointing to this id. void addChangedMaskForLinks(const LLUUID& object_id, U32 mask); @@ -486,6 +513,8 @@ private: // Variables used to track what has changed since the last notify. U32 mModifyMask; changed_items_t mChangedItemIDs; + changed_items_t mAddedItemIDs; + //-------------------------------------------------------------------- // Observers @@ -547,7 +576,7 @@ public: static void processMoveInventoryItem(LLMessageSystem* msg, void**); static void processFetchInventoryReply(LLMessageSystem* msg, void**); protected: - bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting); + bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting, U32 mask = 0x0); //-------------------------------------------------------------------- // Locks @@ -563,12 +592,14 @@ protected: private: std::map mCategoryLock; std::map mItemLock; - - -public: - // *NOTE: DEBUG functionality - void dumpInventory() const; + //-------------------------------------------------------------------- + // Debugging + //-------------------------------------------------------------------- +public: + void dumpInventory() const; + bool validate() const; + /** Miscellaneous ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 20ea15542..88bbaed3d 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -40,23 +40,112 @@ #include "hippogridmanager.h" class AIHTTPTimeoutPolicy; -extern AIHTTPTimeoutPolicy inventoryModelFetchDescendentsResponder_timeout; -extern AIHTTPTimeoutPolicy inventoryModelFetchItemResponder_timeout; +extern AIHTTPTimeoutPolicy BGItemHttpHandler_timeout; +extern AIHTTPTimeoutPolicy BGFolderHttpHandler_timeout; const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; const S32 MAX_FETCH_RETRIES = 10; + +///---------------------------------------------------------------------------- +/// Class ::BGItemHttpHandler +///---------------------------------------------------------------------------- + +// +// Http request handler class for single inventory item requests. +// +// We'll use a handler-per-request pattern here rather than +// a shared handler. Mainly convenient as this was converted +// from a Responder class model. +// +// Derives from and is identical to the normal FetchItemHttpHandler +// except that: 1) it uses the background request object which is +// updated more slowly than the foreground and 2) keeps a count of +// active requests on the LLInventoryModelBackgroundFetch object +// to indicate outstanding operations are in-flight. +// +class BGItemHttpHandler : public LLInventoryModel::FetchItemHttpHandler +{ + LOG_CLASS(BGItemHttpHandler); + +public: + BGItemHttpHandler(const LLSD & request_sd) + : LLInventoryModel::FetchItemHttpHandler(request_sd) + { + LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); + } + + virtual ~BGItemHttpHandler() + { + LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); + } + + /*virtual*/ AICapabilityType capability_type(void) const { return cap_inventory; } + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return BGItemHttpHandler_timeout; } + /*virtual*/ char const* getName(void) const { return "BGItemHttpHandler"; } +protected: + BGItemHttpHandler(const BGItemHttpHandler &); // Not defined + void operator=(const BGItemHttpHandler &); // Not defined +}; + + +///---------------------------------------------------------------------------- +/// Class ::BGFolderHttpHandler +///---------------------------------------------------------------------------- + +// Http request handler class for folders. +// +// Handler for FetchInventoryDescendents2 and FetchLibDescendents2 +// caps requests for folders. +// +class BGFolderHttpHandler : public LLHTTPClient::ResponderWithResult +{ + LOG_CLASS(BGFolderHttpHandler); + +public: + BGFolderHttpHandler(const LLSD & request_sd, const uuid_vec_t & recursive_cats) + : LLHTTPClient::ResponderWithResult(), + mRequestSD(request_sd), + mRecursiveCatUUIDs(recursive_cats) + { + LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); + } + + virtual ~BGFolderHttpHandler() + { + LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); + } + + /*virtual*/ AICapabilityType capability_type(void) const { return cap_inventory; } + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return BGFolderHttpHandler_timeout; } + /*virtual*/ char const* getName(void) const { return "BGFolderHttpHandler"; } + +protected: + BGFolderHttpHandler(const BGFolderHttpHandler &); // Not defined + void operator=(const BGFolderHttpHandler &); // Not defined + BOOL getIsRecursive(const LLUUID& cat_id) const; +private: + /*virtual*/ void httpSuccess(void); + /*virtual*/ void httpFailure(void); + LLSD mRequestSD; + uuid_vec_t mRecursiveCatUUIDs; // hack for storing away which cat fetches are recursive +}; + + +const char * const LOG_INV("Inventory"); + + LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch() : mBackgroundFetchActive(FALSE), mFolderFetchActive(false), + mFetchCount(0), mAllFoldersFetched(FALSE), mRecursiveInventoryFetchStarted(FALSE), mRecursiveLibraryFetchStarted(FALSE), mNumFetchRetries(0), mMinTimeBetweenFetches(0.3f), mMaxTimeBetweenFetches(10.f), - mTimelyFetchPending(FALSE), - mFetchCount(0) + mTimelyFetchPending(FALSE) { } @@ -109,12 +198,24 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const return mFolderFetchActive; } +void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category) +{ + mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category)); +} + +void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category) +{ + mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category)); +} + void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) { - LLViewerInventoryCategory* cat = gInventory.getCategory(id); - if (cat || (id.isNull() && !isEverythingFetched())) - { // it's a folder, do a bulk fetch - LL_DEBUGS("InventoryFetch") << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; + LLViewerInventoryCategory * cat(gInventory.getCategory(id)); + + if (cat || (id.isNull() && ! isEverythingFetched())) + { + // it's a folder, do a bulk fetch + LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; mFolderFetchActive = true; if (id.isNull()) @@ -181,11 +282,13 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched() mAllFoldersFetched = TRUE; } mFolderFetchActive = false; + if (mBackgroundFetchActive) { gIdleCallbacks.deleteFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); - mBackgroundFetchActive = FALSE; } + mBackgroundFetchActive = false; + LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL; } void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) @@ -385,241 +488,41 @@ void LLInventoryModelBackgroundFetch::backgroundFetch() } } -void LLInventoryModelBackgroundFetch::incrFetchCount(S16 fetching) +void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching) { mFetchCount += fetching; if (mFetchCount < 0) { + LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL; mFetchCount = 0; } } -class LLInventoryModelFetchItemResponder : public LLInventoryModel::fetchInventoryResponder -{ -public: - LLInventoryModelFetchItemResponder(const LLSD& request_sd) : LLInventoryModel::fetchInventoryResponder(request_sd) {}; - /*virtual*/ void httpSuccess(void); - /*virtual*/ void httpFailure(void); - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchItemResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "LLInventoryModelFetchItemResponder"; } -}; - -void LLInventoryModelFetchItemResponder::httpSuccess(void) -{ - LLInventoryModel::fetchInventoryResponder::httpSuccess(); - LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); -} - -void LLInventoryModelFetchItemResponder::httpFailure(void) -{ - LLInventoryModel::fetchInventoryResponder::httpFailure(); - LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); -} - -class LLInventoryModelFetchDescendentsResponder : public LLHTTPClient::ResponderWithResult -{ - public: - LLInventoryModelFetchDescendentsResponder(const LLSD& request_sd, uuid_vec_t recursive_cats) : - mRequestSD(request_sd), - mRecursiveCatUUIDs(recursive_cats) - {}; - /*virtual*/ void httpSuccess(void); - /*virtual*/ void httpFailure(void); - /*virtual*/ AICapabilityType capability_type(void) const { return cap_inventory; } - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchDescendentsResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "LLInventoryModelFetchDescendentsResponder"; } - -protected: - BOOL getIsRecursive(const LLUUID& cat_id) const; -private: - LLSD mRequestSD; - uuid_vec_t mRecursiveCatUUIDs; // hack for storing away which cat fetches are recursive -}; - -// If we get back a normal response, handle it here. -void LLInventoryModelFetchDescendentsResponder::httpSuccess(void) -{ - LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); - if (mContent.has("folders")) - { - - for(LLSD::array_const_iterator folder_it = mContent["folders"].beginArray(); - folder_it != mContent["folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - - - //LLUUID agent_id = folder_sd["agent_id"]; - - //if(agent_id != gAgent.getID()) //This should never happen. - //{ - // LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." - // << LL_ENDL; - // break; - //} - - LLUUID parent_id = folder_sd["folder_id"]; - LLUUID owner_id = folder_sd["owner_id"]; - S32 version = (S32)folder_sd["version"].asInteger(); - S32 descendents = (S32)folder_sd["descendents"].asInteger(); - LLPointer tcategory = new LLViewerInventoryCategory(owner_id); - - if (parent_id.isNull()) - { - LLPointer titem = new LLViewerInventoryItem; - for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); - item_it != folder_sd["items"].endArray(); - ++item_it) - { - LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - if (lost_uuid.notNull()) - { - LLSD item = *item_it; - titem->unpackMessage(item); - - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - titem->setParent(lost_uuid); - titem->updateParentOnServer(FALSE); - gInventory.updateItem(titem); - gInventory.notifyObservers(); - - } - } - } - - LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); - if (!pcat) - { - continue; - } - - for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray(); - category_it != folder_sd["categories"].endArray(); - ++category_it) - { - LLSD category = *category_it; - tcategory->fromLLSD(category); - - const BOOL recursive = getIsRecursive(tcategory->getUUID()); - - if (recursive) - { - fetcher->mFetchQueue.push_back(LLInventoryModelBackgroundFetch::FetchQueueInfo(tcategory->getUUID(), recursive)); - } - else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) ) - { - gInventory.updateCategory(tcategory); - } - - } - LLPointer titem = new LLViewerInventoryItem; - for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); - item_it != folder_sd["items"].endArray(); - ++item_it) - { - LLSD item = *item_it; - titem->unpackMessage(item); - - gInventory.updateItem(titem); - } - - // set version and descendentcount according to message. - LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id); - if(cat) - { - cat->setVersion(version); - cat->setDescendentCount(descendents); - cat->determineFolderType(); - } - - } - } - - if (mContent.has("bad_folders")) - { - for(LLSD::array_const_iterator folder_it = mContent["bad_folders"].beginArray(); - folder_it != mContent["bad_folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - - //These folders failed on the dataserver. We probably don't want to retry them. - LL_INFOS() << "Folder " << folder_sd["folder_id"].asString() - << "Error: " << folder_sd["error"].asString() << LL_ENDL; - } - } - - fetcher->incrFetchCount(-1); - - if (fetcher->isBulkFetchProcessingComplete()) - { - LL_INFOS() << "Inventory fetch completed" << LL_ENDL; - fetcher->setAllFoldersFetched(); - } - - gInventory.notifyObservers(); -} - -//If we get back an error (not found, etc...), handle it here -void LLInventoryModelFetchDescendentsResponder::httpFailure(void) -{ - LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); - - LL_INFOS() << "LLInventoryModelFetchDescendentsResponder::error " - << mStatus << ": " << mReason << LL_ENDL; - - fetcher->incrFetchCount(-1); - - if (is_internal_http_error_that_warrants_a_retry(mStatus)) // timed out - { - for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); - folder_it != mRequestSD["folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - LLUUID folder_id = folder_sd["folder_id"]; - const BOOL recursive = getIsRecursive(folder_id); - fetcher->mFetchQueue.push_front(LLInventoryModelBackgroundFetch::FetchQueueInfo(folder_id, recursive)); - } - } - else - { - if (fetcher->isBulkFetchProcessingComplete()) - { - fetcher->setAllFoldersFetched(); - } - } - gInventory.notifyObservers(); -} - -BOOL LLInventoryModelFetchDescendentsResponder::getIsRecursive(const LLUUID& cat_id) const -{ - return (std::find(mRecursiveCatUUIDs.begin(),mRecursiveCatUUIDs.end(), cat_id) != mRecursiveCatUUIDs.end()); -} - // Bundle up a bunch of requests to send all at once. void LLInventoryModelBackgroundFetch::bulkFetch() { //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. //If there are items in mFetchQueue, we want to check the time since the last bulkFetch was //sent. If it exceeds our retry time, go ahead and fire off another batch. - LLViewerRegion* region = gAgent.getRegion(); - if (gDisconnected || !region) return; + LLViewerRegion * region(gAgent.getRegion()); + if (! region || gDisconnected) + { + return; + } + // *TODO: These values could be tweaked at runtime to effect + // a fast/slow fetch throttle. Once login is complete and the scene U32 const max_batch_size = 5; - U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1; + + U32 item_count(0); + U32 folder_count(0); + + const U32 sort_order(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1); uuid_vec_t recursive_cats; - U32 folder_count=0; U32 folder_lib_count=0; - U32 item_count=0; U32 item_lib_count=0; // This function can do up to four requests at once. @@ -633,15 +536,26 @@ void LLInventoryModelBackgroundFetch::bulkFetch() LLSD item_request_body; LLSD item_request_body_lib; - while (!mFetchQueue.empty()) + while (! mFetchQueue.empty()) { - const FetchQueueInfo& fetch_info = mFetchQueue.front(); + const FetchQueueInfo & fetch_info(mFetchQueue.front()); if (fetch_info.mIsCategory) { - const LLUUID &cat_id = fetch_info.mUUID; - if (!cat_id.isNull()) + const LLUUID & cat_id(fetch_info.mUUID); + if (cat_id.isNull()) //DEV-17797 { - const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + 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_request_body["folders"].append(folder_sd); + folder_count++; + } + else + { + const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); if (cat) { @@ -650,9 +564,9 @@ void LLInventoryModelBackgroundFetch::bulkFetch() 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"] = TRUE; //(LLSD::Boolean)sFullFetchStarted; - folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; + 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()) { @@ -680,8 +594,8 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // May already have this folder, but append child folders to list. if (fetch_info.mRecursive) { - LLInventoryModel::cat_array_t* categories; - LLInventoryModel::item_array_t* items; + 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(); @@ -691,13 +605,16 @@ void LLInventoryModelBackgroundFetch::bulkFetch() } } } - if (fetch_info.mRecursive) - recursive_cats.push_back(cat_id); + } + if (fetch_info.mRecursive) + { + recursive_cats.push_back(cat_id); } } else { - LLViewerInventoryItem* itemp = gInventory.getItem(fetch_info.mUUID); + LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + if (itemp) { LLSD item_sd; @@ -735,39 +652,62 @@ void LLInventoryModelBackgroundFetch::bulkFetch() { if (folder_count) { - std::string url = region->getCapability("FetchInventoryDescendents2"); - llassert(!url.empty()); - ++mFetchCount; - LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats); - LLHTTPClient::post_approved(url, folder_request_body, fetcher, approved_folder); + if (folder_request_body["folders"].size()) + { + const std::string url(region->getCapability("FetchInventoryDescendents2")); + + if (! url.empty()) + { + BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body, recursive_cats)); + LLHTTPClient::post_approved(url, folder_request_body, handler, approved_folder); + } + } } if (folder_lib_count) { - std::string url = gAgent.getRegion()->getCapability("FetchLibDescendents2"); - llassert(!url.empty()); - ++mFetchCount; - LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats); - LLHTTPClient::post_approved(url, folder_request_body_lib, fetcher, approved_folder_lib); + if (folder_request_body_lib["folders"].size()) + { + const std::string url(region->getCapability("FetchLibDescendents2")); + + if (! url.empty()) + { + BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body_lib, recursive_cats)); + LLHTTPClient::post_approved(url, folder_request_body_lib, handler, approved_folder_lib); + } + } } + if (item_count) { - std::string url = region->getCapability("FetchInventory2"); - llassert(!url.empty()); - ++mFetchCount; - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"] = item_request_body; - LLHTTPClient::post_approved(url, body, new LLInventoryModelFetchItemResponder(body), approved_item); + if (item_request_body.size()) + { + const std::string url(region->getCapability("FetchInventory2")); + + if (! url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"] = item_request_body; + BGItemHttpHandler * handler(new BGItemHttpHandler(body)); + LLHTTPClient::post_approved(url, body, handler, approved_item); + } + } } if (item_lib_count) { - std::string url = region->getCapability("FetchLib2"); - llassert(!url.empty()); - ++mFetchCount; - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"] = item_request_body_lib; - LLHTTPClient::post_approved(url, body, new LLInventoryModelFetchItemResponder(body), approved_item_lib); + if (item_request_body_lib.size()) + { + const std::string url(region->getCapability("FetchLib2")); + + if (! url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"] = item_request_body_lib; + BGItemHttpHandler * handler(new BGItemHttpHandler(body)); + LLHTTPClient::post_approved(url, body, handler, approved_item_lib); + } + } } mFetchTimer.reset(); } @@ -781,7 +721,8 @@ void LLInventoryModelBackgroundFetch::bulkFetch() bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const { for (fetch_queue_t::const_iterator it = mFetchQueue.begin(); - it != mFetchQueue.end(); ++it) + it != mFetchQueue.end(); + ++it) { const LLUUID& fetch_id = (*it).mUUID; if (gInventory.isObjectDescendentOf(fetch_id, cat_id)) @@ -789,4 +730,175 @@ bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LL } return true; } +// If we get back a normal response, handle it here. +void BGFolderHttpHandler::httpSuccess(void) +{ + LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); + const LLSD& content = mContent; + // in response as an application-level error. + + // Instead, we assume success and attempt to extract information. + if (content.has("folders")) + { + LLSD folders(content["folders"]); + + for (LLSD::array_const_iterator folder_it = folders.beginArray(); + folder_it != folders.endArray(); + ++folder_it) + { + LLSD folder_sd(*folder_it); + + //LLUUID agent_id = folder_sd["agent_id"]; + + //if(agent_id != gAgent.getID()) //This should never happen. + //{ + // LL_WARNS(LOG_INV) << "Got a UpdateInventoryItem for the wrong agent." + // << LL_ENDL; + // break; + //} + + LLUUID parent_id(folder_sd["folder_id"].asUUID()); + LLUUID owner_id(folder_sd["owner_id"].asUUID()); + S32 version(folder_sd["version"].asInteger()); + S32 descendents(folder_sd["descendents"].asInteger()); + LLPointer tcategory = new LLViewerInventoryCategory(owner_id); + + if (parent_id.isNull()) + { + LLSD items(folder_sd["items"]); + LLPointer titem = new LLViewerInventoryItem; + + for (LLSD::array_const_iterator item_it = items.beginArray(); + item_it != items.endArray(); + ++item_it) + { + const LLUUID lost_uuid(gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + + if (lost_uuid.notNull()) + { + LLSD item(*item_it); + + titem->unpackMessage(item); + + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + titem->setParent(lost_uuid); + titem->updateParentOnServer(FALSE); + gInventory.updateItem(titem); + gInventory.notifyObservers(); + } + } + } + + LLViewerInventoryCategory * pcat(gInventory.getCategory(parent_id)); + if (! pcat) + { + continue; + } + + LLSD categories(folder_sd["categories"]); + for (LLSD::array_const_iterator category_it = categories.beginArray(); + category_it != categories.endArray(); + ++category_it) + { + LLSD category(*category_it); + tcategory->fromLLSD(category); + + const bool recursive(getIsRecursive(tcategory->getUUID())); + if (recursive) + { + fetcher->addRequestAtBack(tcategory->getUUID(), recursive, true); + } + else if (! gInventory.isCategoryComplete(tcategory->getUUID())) + { + gInventory.updateCategory(tcategory); + } + } + + LLSD items(folder_sd["items"]); + LLPointer titem = new LLViewerInventoryItem; + for (LLSD::array_const_iterator item_it = items.beginArray(); + item_it != items.endArray(); + ++item_it) + { + LLSD item(*item_it); + titem->unpackMessage(item); + + gInventory.updateItem(titem); + } + + // Set version and descendentcount according to message. + LLViewerInventoryCategory * cat(gInventory.getCategory(parent_id)); + if (cat) + { + cat->setVersion(version); + cat->setDescendentCount(descendents); + cat->determineFolderType(); + } + } + } + + if (content.has("bad_folders")) + { + LLSD bad_folders(content["bad_folders"]); + for (LLSD::array_const_iterator folder_it = bad_folders.beginArray(); + folder_it != bad_folders.endArray(); + ++folder_it) + { + // *TODO: Stop copying data [ed: this isn't copying data] + LLSD folder_sd(*folder_it); + + // These folders failed on the dataserver. We probably don't want to retry them. + LL_WARNS(LOG_INV) << "Folder " << folder_sd["folder_id"].asString() + << "Error: " << folder_sd["error"].asString() << LL_ENDL; + } + } + + if (fetcher->isBulkFetchProcessingComplete()) + { + LL_INFOS() << "Inventory fetch completed" << LL_ENDL; + fetcher->setAllFoldersFetched(); + } + + gInventory.notifyObservers(); +} + +//If we get back an error (not found, etc...), handle it here +void BGFolderHttpHandler::httpFailure(void) +{ + LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); + + LL_INFOS() << "BGFolderHttpHandler::error " + << mStatus << ": " << mReason << LL_ENDL; + + if (is_internal_http_error_that_warrants_a_retry(mStatus)) // timed out + { + for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); + folder_it != mRequestSD["folders"].endArray(); + ++folder_it) + { + LLSD folder_sd(*folder_it); + LLUUID folder_id(folder_sd["folder_id"].asUUID()); + const BOOL recursive = getIsRecursive(folder_id); + fetcher->addRequestAtFront(folder_id, recursive, true); + } + } + else + { + if (fetcher->isBulkFetchProcessingComplete()) + { + fetcher->setAllFoldersFetched(); + } + } + gInventory.notifyObservers(); +} + +BOOL BGFolderHttpHandler::getIsRecursive(const LLUUID& cat_id) const +{ + return (std::find(mRecursiveCatUUIDs.begin(),mRecursiveCatUUIDs.end(), cat_id) != mRecursiveCatUUIDs.end()); +} + diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index 4056bdfbd..a1d85652a 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -39,8 +39,6 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLInventoryModelBackgroundFetch : public LLSingleton { - friend class LLInventoryModelFetchDescendentsResponder; - public: LLInventoryModelBackgroundFetch(); ~LLInventoryModelBackgroundFetch(); @@ -61,16 +59,22 @@ public: bool inventoryFetchInProgress() const; void findLostItems(); - void incrFetchCount(S16 fetching); -protected: + void incrFetchCount(S32 fetching); + bool isBulkFetchProcessingComplete() const; + void setAllFoldersFetched(); + + void addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category); + void addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category); + +protected: void bulkFetch(); void backgroundFetch(); static void backgroundFetchCB(void*); // background fetch idle function - void setAllFoldersFetched(); bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const; + private: BOOL mRecursiveInventoryFetchStarted; BOOL mRecursiveLibraryFetchStarted; @@ -78,7 +82,7 @@ private: BOOL mBackgroundFetchActive; // TRUE if LLInventoryModelBackgroundFetch::backgroundFetchCB is being called from idle(). bool mFolderFetchActive; - S16 mFetchCount; + S32 mFetchCount; BOOL mTimelyFetchPending; S32 mNumFetchRetries; @@ -90,8 +94,10 @@ private: struct FetchQueueInfo { - FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) : - mUUID(id), mRecursive(recursive), mIsCategory(is_category) + FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) + : mUUID(id), + mIsCategory(is_category), + mRecursive(recursive) {} LLUUID mUUID; bool mIsCategory; diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 822bfdc92..c9c91de85 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -38,6 +38,7 @@ #include "llagent.h" #include "llagentwearables.h" #include "llfloater.h" +#include "llfolderview.h" #include "llfocusmgr.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -237,7 +238,7 @@ void fetch_items_from_llsd(const LLSD& items_llsd) if (!url.empty()) { body[i]["agent_id"] = gAgent.getID(); - LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); + LLHTTPClient::post(url, body[i], new LLInventoryModel::FetchItemHttpHandler(body[i])); continue; } @@ -464,42 +465,13 @@ void LLInventoryFetchComboObserver::startFetch() mFetchItems->startFetch(); mFetchDescendents->startFetch(); } - -void LLInventoryExistenceObserver::watchItem(const LLUUID& id) -{ - if (id.notNull()) - { - mMIA.push_back(id); - } -} - -void LLInventoryExistenceObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if (!mMIA.empty()) - { - for (uuid_vec_t::iterator it = mMIA.begin(); it < mMIA.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if (!item) - { - ++it; - continue; - } - mExist.push_back(*it); - it = mMIA.erase(it); - } - if (mMIA.empty()) - { - done(); - } - } -} - +// See comment preceding LLInventoryAddedObserver::changed() for some +// concerns that also apply to this observer. void LLInventoryAddItemByAssetObserver::changed(U32 mask) { - if(!(mask & LLInventoryObserver::ADD)) + if(!(mask & LLInventoryObserver::ADD) || + !(mask & LLInventoryObserver::CREATE) || + !(mask & LLInventoryObserver::UPDATE_CREATE)) { return; } @@ -510,20 +482,12 @@ void LLInventoryAddItemByAssetObserver::changed(U32 mask) return; } - LLMessageSystem* msg = gMessageSystem; - if (!(msg->getMessageName() && (0 == strcmp(msg->getMessageName(), "UpdateCreateInventoryItem")))) + const uuid_set_t& added = gInventory.getAddedIDs(); + for (uuid_set_t::iterator it = added.begin(); it != added.end(); ++it) { - // this is not our message - return; // to prevent a crash. EXT-7921; - } - - LLPointer item = new LLViewerInventoryItem; - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - for(S32 i = 0; i < num_blocks; ++i) - { - item->unpackMessage(msg, _PREHASH_InventoryData, i); + LLInventoryItem *item = gInventory.getItem(*it); const LLUUID& asset_uuid = item->getAssetUUID(); - if (item->getUUID().notNull() && asset_uuid.notNull()) + if (item && item->getUUID().notNull() && asset_uuid.notNull()) { if (isAssetWatched(asset_uuid)) { @@ -532,11 +496,11 @@ void LLInventoryAddItemByAssetObserver::changed(U32 mask) } } } - + if (mAddedItems.size() == mWatchedAssets.size()) { - done(); LL_DEBUGS("Inventory_Move") << "All watched items are added & processed." << LL_ENDL; + done(); mAddedItems.clear(); // Unable to clean watched items here due to somebody can require to check them in current frame. @@ -566,41 +530,28 @@ bool LLInventoryAddItemByAssetObserver::isAssetWatched( const LLUUID& asset_id ) return std::find(mWatchedAssets.begin(), mWatchedAssets.end(), asset_id) != mWatchedAssets.end(); } +// This observer used to explicitly check for whether it was being +// called as a result of an UpdateCreateInventoryItem message. It has +// now been decoupled enough that it's not actually checking the +// message system, but now we have the special UPDATE_CREATE flag +// being used for the same purpose. Fixing this, as we would need to +// do to get rid of the message, is somewhat subtle because there's no +// particular obvious criterion for when creating a new item should +// trigger this observer and when it shouldn't. For example, creating +// a new notecard with new->notecard causes a preview window to pop up +// via the derived class LLOpenTaskOffer, but creating a new notecard +// by copy and paste does not, solely because one goes through +// UpdateCreateInventoryItem and the other doesn't. void LLInventoryAddedObserver::changed(U32 mask) { - if (!(mask & LLInventoryObserver::ADD)) + if (!(mask & LLInventoryObserver::ADD) || + !(mask & LLInventoryObserver::CREATE) || + !(mask & LLInventoryObserver::UPDATE_CREATE)) { return; } - // *HACK: If this was in response to a packet off - // the network, figure out which item was updated. - LLMessageSystem* msg = gMessageSystem; - - std::string msg_name = msg->getMessageName(); - if (msg_name.empty()) - { - return; - } - - // We only want newly created inventory items. JC - if ( msg_name != "UpdateCreateInventoryItem") - { - return; - } - - LLPointer titem = new LLViewerInventoryItem; - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - for (S32 i = 0; i < num_blocks; ++i) - { - titem->unpackMessage(msg, _PREHASH_InventoryData, i); - if (!(titem->getUUID().isNull())) - { - //we don't do anything with null keys - mAdded.push_back(titem->getUUID()); - } - } - if (!mAdded.empty()) + if (!gInventory.getAddedIDs().empty()) { done(); } @@ -633,58 +584,6 @@ void LLInventoryCategoryAddedObserver::changed(U32 mask) } } - -LLInventoryTransactionObserver::LLInventoryTransactionObserver(const LLTransactionID& transaction_id) : - mTransactionID(transaction_id) -{ -} - -void LLInventoryTransactionObserver::changed(U32 mask) -{ - if (mask & LLInventoryObserver::ADD) - { - // This could be it - see if we are processing a bulk update - LLMessageSystem* msg = gMessageSystem; - if (msg->getMessageName() - && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory"))) - { - // we have a match for the message - now check the - // transaction id. - LLUUID id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id); - if (id == mTransactionID) - { - // woo hoo, we found it - uuid_vec_t folders; - uuid_vec_t items; - S32 count; - count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); - S32 i; - for (i = 0; i < count; ++i) - { - msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i); - if (id.notNull()) - { - folders.push_back(id); - } - } - count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); - for (i = 0; i < count; ++i) - { - msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i); - if (id.notNull()) - { - items.push_back(id); - } - } - - // call the derived class the implements this method. - done(folders, items); - } - } - } -} - void LLInventoryCategoriesObserver::changed(U32 mask) { if (!mCategoryMap.size()) @@ -833,3 +732,23 @@ LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( { mItemNameHash.finalize(); } + +void LLScrollOnRenameObserver::changed(U32 mask) +{ + if (mask & LLInventoryObserver::LABEL) + { + 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) + { + mView->scrollToShowSelection(); + + gInventory.removeObserver(this); + delete this; + return; + } + } + } +} diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 13dcd9a12..baa245788 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -58,7 +58,10 @@ public: GESTURE = 64, REBUILD = 128, // Item UI changed (e.g. item type different) SORT = 256, // Folder needs to be resorted. - DESCRIPTION = 0x10000, // Singu extension to keep track of description changes. + CREATE = 512, // With ADD, item has just been created. + // unfortunately a particular message is still associated with some unique semantics. + UPDATE_CREATE = 1024, // With ADD, item added via UpdateCreateInventoryItem + DESCRIPTION = 2048, // Singu extension to keep track of description changes. ALL = 0xffffffff }; LLInventoryObserver(); @@ -152,25 +155,6 @@ protected: LLInventoryFetchDescendentsObserver *mFetchDescendents; }; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryExistenceObserver -// -// Used as a base class for doing something when all the -// observed item ids exist in the inventory somewhere. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryExistenceObserver : public LLInventoryObserver -{ -public: - LLInventoryExistenceObserver() {} - /*virtual*/ void changed(U32 mask); - - void watchItem(const LLUUID& id); -protected: - virtual void done() = 0; - uuid_vec_t mExist; - uuid_vec_t mMIA; -}; - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryMovedObserver // @@ -210,13 +194,11 @@ private: class LLInventoryAddedObserver : public LLInventoryObserver { public: - LLInventoryAddedObserver() : mAdded() {} + LLInventoryAddedObserver() {} /*virtual*/ void changed(U32 mask); protected: virtual void done() = 0; - - uuid_vec_t mAdded; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -241,25 +223,6 @@ protected: cat_vec_t mAddedCategories; }; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryTransactionObserver -// -// Base class for doing something when an inventory transaction completes. -// NOTE: This class is not quite complete. Avoid using unless you fix up its -// functionality gaps. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryTransactionObserver : public LLInventoryObserver -{ -public: - LLInventoryTransactionObserver(const LLTransactionID& transaction_id); - /*virtual*/ void changed(U32 mask); - -protected: - virtual void done(const uuid_vec_t& folders, const uuid_vec_t& items) = 0; - - LLTransactionID mTransactionID; -}; - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryCompletionObserver // @@ -326,4 +289,23 @@ protected: category_map_t mCategoryMap; }; + +class LLFolderView; + +// Force a FolderView to scroll after an item in the corresponding view has been renamed. +class LLScrollOnRenameObserver: public LLInventoryObserver +{ +public: + LLFolderView *mView; + LLUUID mUUID; + + LLScrollOnRenameObserver(const LLUUID& uuid, LLFolderView *view): + mUUID(uuid), + mView(view) + { + } + /* virtual */ void changed(U32 mask); +}; + + #endif // LL_LLINVENTORYOBSERVERS_H diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index fa257b7c3..4fbcb778e 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -145,7 +145,8 @@ LLInventoryPanel::LLInventoryPanel(const std::string& name, mInventory(inventory), mAllowMultiSelect(allow_multi_select), mViewsInitialized(false), - mInvFVBridgeBuilder(NULL) + mInvFVBridgeBuilder(NULL), + mGroupedItemBridge(new LLFolderViewGroupedItemBridge) { mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; @@ -172,7 +173,7 @@ void LLInventoryPanel::buildFolderView() else { root_id = (preferred_type != LLFolderType::FT_NONE) - ? gInventory.findCategoryUUIDForType(preferred_type, false, false) + ? gInventory.findCategoryUUIDForType(preferred_type, false) : gInventory.getCategory(static_cast(mStartFolder)) ? static_cast(mStartFolder) // Singu Note: if start folder is an id of a folder, use it : LLUUID::null; } @@ -695,7 +696,8 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u folder_rect, LLUUID::null, this, - bridge); + bridge, + mGroupedItemBridge); ret->setAllowMultiSelect(mAllowMultiSelect); return ret; } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index c41e1f85d..992c1a761 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -61,6 +61,7 @@ class LLTextBox; class LLIconCtrl; class LLSaveFolderState; class LLInvPanelComplObserver; +class LLFolderViewGroupedItemBridge; class LLInventoryPanel : public LLPanel { @@ -169,6 +170,7 @@ protected: LLHandle mFolderRoot; LLScrollContainer* mScroller; + LLPointer mGroupedItemBridge; /** * Pointer to LLInventoryFVBridgeBuilder. * diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 8b3f28d30..8aaaa7816 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -771,7 +771,8 @@ BOOL LLPanelEditWearable::postBuild() if (mTab = findChild("layer_tabs")) { - for(U32 i = 1; i <= LLAgentWearables::MAX_CLOTHING_PER_TYPE; ++i) + LL_COMPILE_TIME_MESSAGE("layer_tabs needs re-implemented"); + for(U32 i = 1; i <= 6/*LLAgentWearables::MAX_CLOTHING_PER_TYPE*/; ++i) { LLPanel* new_panel = new LLPanel(llformat("%i",i)); mTab->addTabPanel(new_panel, llformat("Layer %i",i)); @@ -1008,15 +1009,13 @@ void LLPanelEditWearable::refreshWearables(bool force_immediate) U32 index; if (mPendingWearable) { - index = gAgentWearables.getWearableIndex(mPendingWearable); - if (index == LLAgentWearables::MAX_CLOTHING_PER_TYPE) + if (!gAgentWearables.getWearableIndex(mPendingWearable, index)) return; mPendingWearable = NULL; } else { - index = gAgentWearables.getWearableIndex(getWearable()); - if (index == LLAgentWearables::MAX_CLOTHING_PER_TYPE) + if (!gAgentWearables.getWearableIndex(mPendingWearable, index)) { index = gAgentWearables.getWearableCount(mType); if (index) @@ -1026,7 +1025,8 @@ void LLPanelEditWearable::refreshWearables(bool force_immediate) if (mTab) { - for(U32 i = 0; i < LLAgentWearables::MAX_CLOTHING_PER_TYPE; ++i) + LL_COMPILE_TIME_MESSAGE("layer_tabs needs re-implemented"); + for(U32 i = 0; i < 6/*LLAgentWearables::MAX_CLOTHING_PER_TYPE*/; ++i) { mTab->enableTabButton(i, i < gAgentWearables.getWearableCount(mType)); } @@ -1168,10 +1168,13 @@ void LLPanelEditWearable::setNewImageID(ETextureIndex te_index, LLUUID const& uu } if (getWearable()) { - U32 index = gAgentWearables.getWearableIndex(getWearable()); - gAgentAvatarp->setLocalTexture(te_index, image, FALSE, index); - LLVisualParamHint::requestHintUpdates(); - gAgentAvatarp->wearableUpdated(mType, FALSE); + U32 index; + if (gAgentWearables.getWearableIndex(getWearable(), index)) + { + gAgentAvatarp->setLocalTexture(te_index, image, FALSE, index); + LLVisualParamHint::requestHintUpdates(); + gAgentAvatarp->wearableUpdated(mType, FALSE); + } } if (mType == LLWearableType::WT_ALPHA && image->getID() != IMG_INVISIBLE) { @@ -1230,7 +1233,8 @@ void LLPanelEditWearable::saveChanges(bool force_save_as, std::string new_name) return; } - U32 index = gAgentWearables.getWearableIndex(getWearable()); + U32 index; + gAgentWearables.getWearableIndex(getWearable(), index); // Find an existing link to this wearable's inventory item, if any, and its description field. LLInventoryItem *link_item = NULL; @@ -1271,15 +1275,11 @@ void LLPanelEditWearable::saveChanges(bool force_save_as, std::string new_name) if (link_item) { // Create new link - link_inventory_item( gAgent.getID(), - link_item->getLinkedUUID(), - LLAppearanceMgr::instance().getCOF(), - link_item->getName(), - description, - LLAssetType::AT_LINK, + link_inventory_object( LLAppearanceMgr::instance().getCOF(), + link_item, NULL); - // Remove old link - gInventory.purgeObject(link_item->getUUID()); + + remove_inventory_item(link_item, NULL); } gAgentWearables.saveWearable(mType, index, TRUE, new_name); } @@ -1543,9 +1543,12 @@ void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, LLAvatarAppearanc mPreviousAlphaTexture[te] = lto->getID(); LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture( IMG_INVISIBLE ); - U32 index = gAgentWearables.getWearableIndex(getWearable()); - gAgentAvatarp->setLocalTexture(te, image, FALSE, index); - gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE); + U32 index; + if (gAgentWearables.getWearableIndex(getWearable(), index)) + { + gAgentAvatarp->setLocalTexture(te, image, FALSE, index); + gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE); + } } else { @@ -1563,9 +1566,12 @@ void LLPanelEditWearable::onInvisibilityCommit(LLUICtrl* ctrl, LLAvatarAppearanc if (!image) return; - U32 index = gAgentWearables.getWearableIndex(getWearable()); - gAgentAvatarp->setLocalTexture(te, image, FALSE, index); - gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE); + U32 index; + if (gAgentWearables.getWearableIndex(getWearable(), index)) + { + gAgentAvatarp->setLocalTexture(te, image, FALSE, index); + gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE); + } } } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 3c80bdf13..02f3eedf0 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -903,6 +903,11 @@ void LLInventoryView::toggleFindOptions() } } +LLFolderView* LLInventoryView::getRootFolder() const +{ + return mActivePanel ? (mActivePanel->getRootFolder()) : NULL; +} + void LLInventoryView::setSelectCallback(const LLFolderView::signal_t::slot_type& cb) { getChild("All Items")->setSelectCallback(cb); diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index f006aa904..0b258ac8b 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -87,6 +87,7 @@ public: LLInventoryPanel* getPanel() { return mActivePanel; } LLInventoryPanel* getActivePanel() { return mActivePanel; } const LLInventoryPanel* getActivePanel() const { return mActivePanel; } + LLFolderView* getRootFolder() const; const std::string& getFilterText() const { return mFilterText; } diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.cpp b/indra/newview/llpanelmarketplaceoutboxinventory.cpp index 1103312f5..9932c8641 100644 --- a/indra/newview/llpanelmarketplaceoutboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceoutboxinventory.cpp @@ -68,7 +68,7 @@ void LLOutboxInventoryPanel::buildFolderView(/*const LLInventoryPanel::Params& p // Determine the root folder in case specified, and // build the views starting with that folder. - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (root_id == LLUUID::null) { diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 02322ed06..59cbc06d5 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -495,13 +495,6 @@ void LLPreview::onDiscardBtn(void* data) self->mForceClose = TRUE; self->close(); - // Delete the item entirely - /* - item->removeFromServer(); - gInventory.deleteObject(item->getUUID()); - gInventory.notifyObservers(); - */ - // Move the item to the trash LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); if (item->getParentUUID() != trash_id) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 7d509fb0e..0476a8915 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -380,6 +380,13 @@ void update_texture_fetch() gTextureList.updateImages(0.10f); } +void set_flags_and_update_appearance() +{ + LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); + LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op); +} + + void hooked_process_sound_trigger(LLMessageSystem *msg, void **) { process_sound_trigger(msg,NULL); @@ -2247,12 +2254,17 @@ bool idle_startup() // This method MUST be called before gInventory.findCategoryUUIDForType because of // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. gInventory.buildParentChildMap(); + gInventory.createCommonSystemCategories(); + + // It's debatable whether this flag is a good idea - sets all + // bits, and in general it isn't true that inventory + // initialization generates all types of changes. Maybe add an + // INITIALIZE mask bit instead? + gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null); + gInventory.notifyObservers(); + display_startup(); - /*llinfos << "Setting Inventory changed mask and notifying observers" << llendl; - gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null); - gInventory.notifyObservers();*/ - //all categories loaded. lets create "My Favorites" category gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); @@ -2565,15 +2577,35 @@ bool idle_startup() // Start loading the wearables, textures, gestures LLStartUp::loadInitialOutfit( sInitialOutfit, sInitialOutfitGender ); } + // If not first login, we need to fetch COF contents and + // compute appearance from that. + if (isAgentAvatarValid() && !gAgent.isFirstLogin() && !gAgent.isGenderChosen()) + { + gAgentWearables.notifyLoadingStarted(); + gAgent.setGenderChosen(TRUE); + gAgentWearables.sendDummyAgentWearablesUpdate(); + callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance); + } display_startup(); // wait precache-delay and for agent's avatar or a lot longer. - if(((timeout_frac > 1.f) && isAgentAvatarValid()) - || (timeout_frac > 3.f)) + if((timeout_frac > 1.f) && isAgentAvatarValid()) { LLStartUp::setStartupState( STATE_WEARABLES_WAIT ); } + else if (timeout_frac > 10.f) + { + // If we exceed the wait above while isAgentAvatarValid is + // not true yet, we will change startup state and + // eventually (once avatar does get created) wind up at + // the gender chooser. This should occur only in very + // unusual circumstances, so set the timeout fairly high + // to minimize mistaken hits here. + LL_WARNS() << "Wait for valid avatar state exceeded " + << timeout.getElapsedTimeF32() << " will invoke gender chooser" << LL_ENDL; + LLStartUp::setStartupState( STATE_WEARABLES_WAIT ); + } else { update_texture_fetch(); @@ -4096,7 +4128,10 @@ bool process_login_success_response(std::string& password, U32& first_sim_size_x flag = login_flags["gendered"].asString(); if(flag == "Y") { - gAgent.setGenderChosen(TRUE); + // We don't care about this flag anymore; now base whether + // outfit is chosen on COF contents, initial outfit + // requested and available, etc. + //gAgent.setGenderChosen(TRUE); } flag = login_flags["daylight_savings"].asString(); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 0c3d91236..8ee9ada0e 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -803,7 +803,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if ( !handled ) { // Disallow drag and drop to 3D from the outbox - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (outbox_id.notNull()) { for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b9d9d378e..b5456d30b 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -43,6 +43,7 @@ #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" #include "llgesturemgr.h" +#include "llinventoryclipboard.h" #include "llinventorybridge.h" #include "llinventorypanel.h" @@ -61,16 +62,23 @@ #include "llviewermessage.h" #include "llavatarnamecache.h" +#include "llsdutil.h" + #include "llfloatercustomize.h" // #include "llappviewer.h" // System Folders bool use_http_inventory(); // UseHTTPInventory replacement // -// Two do-nothing ops for use in callbacks. +// do-nothing ops for use in callbacks. void no_op_inventory_func(const LLUUID&) {} +void no_op_llsd_func(const LLSD&) {} void no_op() {} +static const char * const LOG_INV("Inventory"); +static const char * const LOG_LOCAL("InventoryLocalize"); +static const char * const LOG_NOTECARD("copy_inventory_from_notecard"); + ///---------------------------------------------------------------------------- /// Helper class to store special inventory item names and their localized values. ///---------------------------------------------------------------------------- @@ -185,7 +193,7 @@ public: */ bool localizeInventoryObjectName(std::string& object_name) { - LL_DEBUGS("InventoryLocalize") << "Searching for localization: " << object_name << LL_ENDL; + LL_DEBUGS(LOG_LOCAL) << "Searching for localization: " << object_name << LL_ENDL; std::map::const_iterator dictionary_iter = mInventoryItemsDict.find(object_name); @@ -193,7 +201,7 @@ public: if(found) { object_name = dictionary_iter->second; - LL_DEBUGS("InventoryLocalize") << "Found, new name is: " << object_name << LL_ENDL; + LL_DEBUGS(LOG_LOCAL) << "Found, new name is: " << object_name << LL_ENDL; } return found; } @@ -293,24 +301,6 @@ void LLViewerInventoryItem::cloneViewerItem(LLPointer& ne } } -void LLViewerInventoryItem::removeFromServer() -{ - LL_DEBUGS() << "Removing inventory item " << mUUID << " from server." - << LL_ENDL; - - LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1); - gInventory.accountForUpdate(up); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RemoveInventoryItem); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_ItemID, mUUID); - gAgent.sendReliableMessage(); -} - void LLViewerInventoryItem::updateServer(BOOL is_new) const { if(getWearableType() == LLWearableType::WT_UNKNOWN) @@ -322,7 +312,7 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const if(!mIsComplete) { // *FIX: deal with this better. - LL_WARNS() << "LLViewerInventoryItem::updateServer() - for incomplete item" + LL_ERRS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for incomplete item" << LL_ENDL; LLNotificationsUtil::add("IncompleteInventoryItem"); return; @@ -330,8 +320,9 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const if(gAgent.getID() != mPermissions.getOwner()) { // *FIX: deal with this better. - LL_WARNS() << "LLViewerInventoryItem::updateServer() - for unowned item" - << LL_ENDL; + LL_WARNS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for unowned item " + << ll_pretty_print_sd(this->asLLSD()) + << LL_ENDL; return; } LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0); @@ -351,9 +342,9 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const void LLViewerInventoryItem::fetchFromServer(void) const { - if(!mIsComplete) + if (!mIsComplete) { - std::string url; + std::string url; if (use_http_inventory()) { @@ -361,7 +352,7 @@ void LLViewerInventoryItem::fetchFromServer(void) const // we have to check region. It can be null after region was destroyed. See EXT-245 if (region) { - if(gAgent.getID() != mPermissions.getOwner()) + if (gAgent.getID() != mPermissions.getOwner()) { url = region->getCapability("FetchLib2"); } @@ -372,41 +363,36 @@ void LLViewerInventoryItem::fetchFromServer(void) const } else { - LL_WARNS() << "Agent Region is absent" << LL_ENDL; + LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; + } + + if (!url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"][0]["owner_id"] = mPermissions.getOwner(); + body["items"][0]["item_id"] = mUUID; + + LLHTTPClient::post(url, body, new LLInventoryModel::FetchItemHttpHandler(body)); + } + else + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("FetchInventory"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("OwnerID", mPermissions.getOwner()); + msg->addUUID("ItemID", mUUID); + gAgent.sendReliableMessage(); } } - - if (!url.empty()) - { - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"][0]["owner_id"] = mPermissions.getOwner(); - body["items"][0]["item_id"] = mUUID; - - LLHTTPClient::post(url, body, new LLInventoryModel::fetchInventoryResponder(body)); - } - else - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventory"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("OwnerID", mPermissions.getOwner()); - msg->addUUID("ItemID", mUUID); - gAgent.sendReliableMessage(); - } - } - else - { - // *FIX: this can be removed after a bit. - LL_WARNS() << "request to fetch complete item" << LL_ENDL; } } // virtual -BOOL LLViewerInventoryItem::unpackMessage(LLSD item) +BOOL LLViewerInventoryItem::unpackMessage(const LLSD& item) { BOOL rv = LLInventoryItem::fromLLSD(item); @@ -561,6 +547,15 @@ void LLViewerInventoryCategory::copyViewerCategory(const LLViewerInventoryCatego } +void LLViewerInventoryCategory::packMessage(LLMessageSystem* msg) const +{ + msg->addUUIDFast(_PREHASH_FolderID, mUUID); + msg->addUUIDFast(_PREHASH_ParentID, mParentUUID); + S8 type = static_cast(mPreferredType); + msg->addS8Fast(_PREHASH_Type, type); + msg->addStringFast(_PREHASH_Name, mName); +} + void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const { LLMessageSystem* msg = gMessageSystem; @@ -599,30 +594,14 @@ void LLViewerInventoryCategory::updateServer(BOOL is_new) const gAgent.sendReliableMessage(); } -void LLViewerInventoryCategory::removeFromServer( void ) +S32 LLViewerInventoryCategory::getVersion() const { - LL_INFOS() << "Removing inventory category " << mUUID << " from server." - << LL_ENDL; - // communicate that change with the server. -#ifndef DELETE_SYSTEM_FOLDERS - if(LLFolderType::lookupIsProtectedType(mPreferredType)) - { - LLNotificationsUtil::add("CannotRemoveProtectedCategories"); - return; - } -#endif + return mVersion; +} - LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1); - gInventory.accountForUpdate(up); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RemoveInventoryFolder); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, mUUID); - gAgent.sendReliableMessage(); +void LLViewerInventoryCategory::setVersion(S32 version) +{ + mVersion = version; } bool LLViewerInventoryCategory::fetch() @@ -631,7 +610,7 @@ bool LLViewerInventoryCategory::fetch() (!mDescendentsRequested.getStarted() || mDescendentsRequested.hasExpired())) //Expired check prevents multiple downloads. { - LL_DEBUGS("InventoryFetch") << "Fetching category children: " << mName << ", UUID: " << mUUID << LL_ENDL; + LL_DEBUGS(LOG_INV) << "Fetching category children: " << mName << ", UUID: " << mUUID << LL_ENDL; const F32 FETCH_TIMER_EXPIRY = 10.0f; mDescendentsRequested.start(FETCH_TIMER_EXPIRY); @@ -655,7 +634,7 @@ bool LLViewerInventoryCategory::fetch() } else { - LL_WARNS() << "agent region is null" << LL_ENDL; + LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL; } if (!url.empty() && use_http_inventory()) //Capability found and HTTP inventory enabled. Build up LLSD and use it. { @@ -665,7 +644,7 @@ bool LLViewerInventoryCategory::fetch() { //We don't have a capability or the use of HTTP inventory is disabled, use the old system. if (use_http_inventory()) { - LL_INFOS() << "FetchInventoryDescendents2 capability not found. Using UDP message." << LL_ENDL; + LL_INFOS(LOG_INV) << "FetchInventoryDescendents2 capability not found. Using deprecated UDP message." << LL_ENDL; } LLMessageSystem* msg = gMessageSystem; msg->newMessage("FetchInventoryDescendents"); @@ -686,6 +665,19 @@ bool LLViewerInventoryCategory::fetch() return false; } +S32 LLViewerInventoryCategory::getViewerDescendentCount() const +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(getUUID(), cats, items); + S32 descendents_actual = 0; + if(cats && items) + { + descendents_actual = cats->size() + items->size(); + } + return descendents_actual; +} + bool LLViewerInventoryCategory::importFileLocal(LLFILE* fp) { // *NOTE: This buffer size is hard coded into scanf() below. @@ -748,8 +740,8 @@ bool LLViewerInventoryCategory::importFileLocal(LLFILE* fp) } else { - LL_WARNS() << "unknown keyword '" << keyword - << "' in inventory import category " << mUUID << LL_ENDL; + LL_WARNS(LOG_INV) << "unknown keyword '" << keyword + << "' in inventory import category " << mUUID << LL_ENDL; } } return true; @@ -851,6 +843,21 @@ void LLViewerInventoryCategory::localizeName() LLLocalizedInventoryItemsDictionary::getInstance()->localizeInventoryObjectName(mName); } +// virtual +BOOL LLViewerInventoryCategory::unpackMessage(const LLSD& category) +{ + BOOL rv = LLInventoryCategory::fromLLSD(category); + localizeName(); + return rv; +} + +// virtual +void LLViewerInventoryCategory::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num) +{ + LLInventoryCategory::unpackMessage(msg, block, block_num); + localizeName(); +} + ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- @@ -862,7 +869,7 @@ LLInventoryCallbackManager::LLInventoryCallbackManager() : { if( sInstance != NULL ) { - LL_WARNS() << "LLInventoryCallbackManager::LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; + LL_WARNS(LOG_INV) << "LLInventoryCallbackManager::LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; return; } sInstance = this; @@ -872,7 +879,7 @@ LLInventoryCallbackManager::~LLInventoryCallbackManager() { if( sInstance != this ) { - LL_WARNS() << "LLInventoryCallbackManager::~LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; + LL_WARNS(LOG_INV) << "LLInventoryCallbackManager::~LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; return; } sInstance = NULL; @@ -1108,70 +1115,137 @@ void copy_inventory_item( gAgent.sendReliableMessage(); } -void link_inventory_item( - const LLUUID& agent_id, - const LLUUID& item_id, - const LLUUID& parent_id, - const std::string& new_name, - const std::string& new_description, - const LLAssetType::EType asset_type, - LLPointer cb) +// Create link to single inventory object. +void link_inventory_object(const LLUUID& category, + LLConstPointer baseobj, + LLPointer cb) { - const LLInventoryObject *baseobj = gInventory.getObject(item_id); if (!baseobj) { - LL_WARNS() << "attempt to link to unknown item, linked-to-item's itemID " << item_id << LL_ENDL; - return; - } - if (baseobj && baseobj->getIsLinkType()) - { - LL_WARNS() << "attempt to create a link to a link, linked-to-item's itemID " << item_id << LL_ENDL; + LL_WARNS(LOG_INV) << "Attempt to link to non-existent object" << LL_ENDL; return; } - if (baseobj && !LLAssetType::lookupCanLink(baseobj->getType())) + LLInventoryObject::const_object_list_t obj_array; + obj_array.push_back(baseobj); + link_inventory_array(category, obj_array, cb); +} + +void link_inventory_object(const LLUUID& category, + const LLUUID& id, + LLPointer cb) +{ + LLConstPointer baseobj = gInventory.getObject(id); + link_inventory_object(category, baseobj, cb); +} + +// Create links to all listed inventory objects. +void link_inventory_array(const LLUUID& category, + LLInventoryObject::const_object_list_t& baseobj_array, + LLPointer cb) +{ +#ifndef LL_RELEASE_FOR_DOWNLOAD + const LLViewerInventoryCategory *cat = gInventory.getCategory(category); + const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; +#endif + LLInventoryObject::const_object_list_t::const_iterator it = baseobj_array.begin(); + LLInventoryObject::const_object_list_t::const_iterator end = baseobj_array.end(); + LLSD links = LLSD::emptyArray(); + for (; it != end; ++it) { - // Fail if item can be found but is of a type that can't be linked. - // Arguably should fail if the item can't be found too, but that could - // be a larger behavioral change. - LL_WARNS() << "attempt to link an unlinkable item, type = " << baseobj->getActualType() << LL_ENDL; - return; - } - - LLUUID transaction_id; - LLInventoryType::EType inv_type = LLInventoryType::IT_NONE; - if (dynamic_cast(baseobj)) - { - inv_type = LLInventoryType::IT_CATEGORY; - } - else - { - const LLViewerInventoryItem *baseitem = dynamic_cast(baseobj); - if (baseitem) + const LLInventoryObject* baseobj = *it; + if (!baseobj) { - inv_type = baseitem->getInventoryType(); + LL_WARNS(LOG_INV) << "attempt to link to unknown object" << LL_ENDL; + continue; + } + + if (!LLAssetType::lookupCanLink(baseobj->getType())) + { + // Fail if item can be found but is of a type that can't be linked. + // Arguably should fail if the item can't be found too, but that could + // be a larger behavioral change. + LL_WARNS(LOG_INV) << "attempt to link an unlinkable object, type = " << baseobj->getActualType() << LL_ENDL; + continue; + } + + LLInventoryType::EType inv_type = LLInventoryType::IT_NONE; + LLAssetType::EType asset_type = LLAssetType::AT_NONE; + std::string new_desc; + LLUUID linkee_id; + if (dynamic_cast(baseobj)) + { + inv_type = LLInventoryType::IT_CATEGORY; + asset_type = LLAssetType::AT_LINK_FOLDER; + linkee_id = baseobj->getUUID(); + } + else + { + const LLViewerInventoryItem *baseitem = dynamic_cast(baseobj); + if (baseitem) + { + inv_type = baseitem->getInventoryType(); + new_desc = baseitem->getActualDescription(); + switch (baseitem->getActualType()) + { + case LLAssetType::AT_LINK: + case LLAssetType::AT_LINK_FOLDER: + linkee_id = baseobj->getLinkedUUID(); + asset_type = baseitem->getActualType(); + break; + default: + linkee_id = baseobj->getUUID(); + asset_type = LLAssetType::AT_LINK; + break; + } + } + else + { + LL_WARNS(LOG_INV) << "could not convert object into an item or category: " << baseobj->getUUID() << LL_ENDL; + continue; + } + } + + LLSD link = LLSD::emptyMap(); + link["linked_id"] = linkee_id; + link["type"] = (S8)asset_type; + link["inv_type"] = (S8)inv_type; + link["name"] = baseobj->getName(); + link["desc"] = new_desc; + links.append(link); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + LL_DEBUGS(LOG_INV) << "Linking Object [ name:" << baseobj->getName() + << " UUID:" << baseobj->getUUID() + << " ] into Category [ name:" << cat_name + << " UUID:" << category << " ] " << LL_ENDL; +#endif + } + { + LLMessageSystem* msg = gMessageSystem; + for (LLSD::array_iterator iter = links.beginArray(); iter != links.endArray(); ++iter ) + { + msg->newMessageFast(_PREHASH_LinkInventoryItem); + msg->nextBlock(_PREHASH_AgentData); + { + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + msg->nextBlock(_PREHASH_InventoryBlock); + { + LLSD link = (*iter); + msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); + msg->addUUIDFast(_PREHASH_FolderID, category); + msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); + msg->addUUIDFast(_PREHASH_OldItemID, link["linked_id"].asUUID()); + msg->addS8Fast(_PREHASH_Type, link["type"].asInteger()); + msg->addS8Fast(_PREHASH_InvType, link["inv_type"].asInteger()); + msg->addStringFast(_PREHASH_Name, link["name"].asString()); + msg->addStringFast(_PREHASH_Description, link["desc"].asString()); + } + gAgent.sendReliableMessage(); } } - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_LinkInventoryItem); - msg->nextBlock(_PREHASH_AgentData); - { - msg->addUUIDFast(_PREHASH_AgentID, agent_id); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - msg->nextBlock(_PREHASH_InventoryBlock); - { - msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); - msg->addUUIDFast(_PREHASH_FolderID, parent_id); - msg->addUUIDFast(_PREHASH_TransactionID, transaction_id); - msg->addUUIDFast(_PREHASH_OldItemID, item_id); - msg->addS8Fast(_PREHASH_Type, (S8)asset_type); - msg->addS8Fast(_PREHASH_InvType, (S8)inv_type); - msg->addStringFast(_PREHASH_Name, new_name); - msg->addStringFast(_PREHASH_Description, new_description); - } - gAgent.sendReliableMessage(); } void move_inventory_item( @@ -1195,6 +1269,343 @@ void move_inventory_item( gAgent.sendReliableMessage(); } +// Should call this with an update_item that's been copied and +// modified from an original source item, rather than modifying the +// source item directly. +void update_inventory_item( + LLViewerInventoryItem *update_item, + LLPointer cb) +{ + const LLUUID& item_id = update_item->getUUID(); + { + LLPointer obj = gInventory.getItem(item_id); + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (update_item ? update_item->getName() : "(NOT FOUND)") << LL_ENDL; + if(obj) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_TransactionID, update_item->getTransactionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_CallbackID, 0); + update_item->packMessage(msg); + gAgent.sendReliableMessage(); + + LLInventoryModel::LLCategoryUpdate up(update_item->getParentUUID(), 0); + gInventory.accountForUpdate(up); + gInventory.updateItem(update_item); + if (cb) + { + cb->fire(item_id); + } + } + } +} + +// Note this only supports updating an existing item. Goes through AISv3 +// code path where available. Not all uses of item->updateServer() can +// easily be switched to this paradigm. +void update_inventory_item( + const LLUUID& item_id, + const LLSD& updates, + LLPointer cb) +{ + { + LLPointer obj = gInventory.getItem(item_id); + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; + if(obj) + { + LLPointer new_item(new LLViewerInventoryItem); + new_item->copyViewerItem(obj); + new_item->fromLLSD(updates,false); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_TransactionID, new_item->getTransactionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_CallbackID, 0); + new_item->packMessage(msg); + gAgent.sendReliableMessage(); + + LLInventoryModel::LLCategoryUpdate up(new_item->getParentUUID(), 0); + gInventory.accountForUpdate(up); + gInventory.updateItem(new_item); + if (cb) + { + cb->fire(item_id); + } + } + } +} + +void update_inventory_category( + const LLUUID& cat_id, + const LLSD& updates, + LLPointer cb) +{ + LLPointer obj = gInventory.getCategory(cat_id); + LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; + if(obj) + { + if (LLFolderType::lookupIsProtectedType(obj->getPreferredType())) + { + LLNotificationsUtil::add("CannotModifyProtectedCategories"); + return; + } + + LLPointer new_cat = new LLViewerInventoryCategory(obj); + new_cat->fromLLSD(updates); + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_FolderData); + new_cat->packMessage(msg); + gAgent.sendReliableMessage(); + + LLInventoryModel::LLCategoryUpdate up(new_cat->getParentUUID(), 0); + gInventory.accountForUpdate(up); + gInventory.updateCategory(new_cat); + if (cb) + { + cb->fire(cat_id); + } + } + } +} + +void remove_inventory_items( + LLInventoryObject::object_list_t& items_to_kill, + LLPointer cb + ) +{ + for (LLInventoryObject::object_list_t::iterator it = items_to_kill.begin(); + it != items_to_kill.end(); + ++it) + { + remove_inventory_item(*it, cb); + } +} + +void remove_inventory_item( + const LLUUID& item_id, + LLPointer cb, + bool immediate_delete) +{ + LLPointer obj = gInventory.getItem(item_id); + if (obj) + { + remove_inventory_item(obj, cb, immediate_delete); + } + else + { + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << "(NOT FOUND)" << LL_ENDL; + } +} + +void remove_inventory_item( + LLPointer obj, + LLPointer cb, + bool immediate_delete) +{ + if(obj) + { + const LLUUID item_id(obj->getUUID()); + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << obj->getName() << LL_ENDL; + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RemoveInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_ItemID, item_id); + gAgent.sendReliableMessage(); + + // Update inventory and call callback immediately since + // message-based system has no callback mechanism (!) + gInventory.onObjectDeletedFromServer(item_id); + if (cb) + { + cb->fire(item_id); + } + } + } + else + { + // *TODO: Clean up callback? + LL_WARNS(LOG_INV) << "remove_inventory_item called for invalid or nonexistent item." << LL_ENDL; + } +} + +class LLRemoveCategoryOnDestroy: public LLInventoryCallback +{ +public: + LLRemoveCategoryOnDestroy(const LLUUID& cat_id, LLPointer cb): + mID(cat_id), + mCB(cb) + { + } + /* virtual */ void fire(const LLUUID& item_id) {} + ~LLRemoveCategoryOnDestroy() + { + LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(mID); + if(children != LLInventoryModel::CHILDREN_NO) + { + LL_WARNS(LOG_INV) << "remove descendents failed, cannot remove category " << LL_ENDL; + } + else + { + remove_inventory_category(mID, mCB); + } + } +private: + LLUUID mID; + LLPointer mCB; +}; + +void remove_inventory_category( + const LLUUID& cat_id, + LLPointer cb) +{ + LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] " << LL_ENDL; + LLPointer obj = gInventory.getCategory(cat_id); + if(obj) + { + if(LLFolderType::lookupIsProtectedType(obj->getPreferredType())) + { + LLNotificationsUtil::add("CannotRemoveProtectedCategories"); + return; + } + { + // RemoveInventoryFolder does not remove children, so must + // clear descendents first. + LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(cat_id); + if(children != LLInventoryModel::CHILDREN_NO) + { + LL_DEBUGS(LOG_INV) << "Will purge descendents first before deleting category " << cat_id << LL_ENDL; + LLPointer wrap_cb = new LLRemoveCategoryOnDestroy(cat_id, cb); + purge_descendents_of(cat_id, wrap_cb); + return; + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RemoveInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, cat_id); + gAgent.sendReliableMessage(); + + // Update inventory and call callback immediately since + // message-based system has no callback mechanism (!) + gInventory.onObjectDeletedFromServer(cat_id); + if (cb) + { + cb->fire(cat_id); + } + } + } + else + { + LL_WARNS(LOG_INV) << "remove_inventory_category called for invalid or nonexistent item " << cat_id << LL_ENDL; + } +} + +void remove_inventory_object( + const LLUUID& object_id, + LLPointer cb) +{ + if (gInventory.getCategory(object_id)) + { + remove_inventory_category(object_id, cb); + } + else + { + remove_inventory_item(object_id, cb); + } +} + +// This is a method which collects the descendents of the id +// provided. If the category is not found, no action is +// taken. This method goes through the long winded process of +// cancelling any calling cards, removing server representation of +// folders, items, etc in a fairly efficient manner. +void purge_descendents_of(const LLUUID& id, LLPointer cb) +{ + LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(id); + if(children == LLInventoryModel::CHILDREN_NO) + { + LL_DEBUGS(LOG_INV) << "No descendents to purge for " << id << LL_ENDL; + return; + } + LLPointer cat = gInventory.getCategory(id); + if (cat.notNull()) + { + if (LLInventoryClipboard::instance().hasContents() && LLInventoryClipboard::instance().isCutMode()) + { + // Something on the clipboard is in "cut mode" and needs to be preserved + LL_DEBUGS(LOG_INV) << "purge_descendents_of clipboard case " << cat->getName() + << " iterate and purge non hidden items" << LL_ENDL; + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + // Get the list of direct descendants in tha categoy passed as argument + gInventory.getDirectDescendentsOf(id, categories, items); + std::vector list_uuids; + // Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently) + // Note: we need to do that shallow copy as purging things will invalidate the categories or items lists + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it != items->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + // Iterate through the list and only purge the UUIDs that are not on the clipboard + for (std::vector::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it) + { + if (!LLInventoryClipboard::instance().isOnClipboard(*it)) + { + remove_inventory_object(*it, NULL); + } + } + } + else + { + { + // Fast purge + LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL; + + // send it upstream + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("PurgeInventoryDescendents"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("FolderID", id); + gAgent.sendReliableMessage(); + + // Update model immediately because there is no callback mechanism. + gInventory.onDescendentsPurgedFromServer(id); + if (cb) + { + cb->fire(id); + } + } + } + } +} + const LLUUID get_folder_by_itemtype(const LLInventoryItem *src) { LLUUID retval = LLUUID::null; @@ -1215,9 +1626,9 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, { if (NULL == src) { - LL_WARNS("copy_inventory_from_notecard") << "Null pointer to item was passed for object_id " - << object_id << " and notecard_inv_id " - << notecard_inv_id << LL_ENDL; + LL_WARNS(LOG_NOTECARD) << "Null pointer to item was passed for object_id " + << object_id << " and notecard_inv_id " + << notecard_inv_id << LL_ENDL; return; } @@ -1237,9 +1648,9 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, if (! viewer_region) { - LL_WARNS("copy_inventory_from_notecard") << "Can't find region from object_id " - << object_id << " or gAgent" - << LL_ENDL; + LL_WARNS(LOG_NOTECARD) << "Can't find region from object_id " + << object_id << " or gAgent" + << LL_ENDL; return; } @@ -1247,8 +1658,8 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, std::string url = viewer_region->getCapability("CopyInventoryFromNotecard"); if (url.empty()) { - LL_WARNS("copy_inventory_from_notecard") << "There is no 'CopyInventoryFromNotecard' capability" - << " for region: " << viewer_region->getName() + LL_WARNS(LOG_NOTECARD) << "There is no 'CopyInventoryFromNotecard' capability" + << " for region: " << viewer_region->getName() << LL_ENDL; return; } @@ -1317,6 +1728,45 @@ void create_new_item(const std::string& name, cb); } +void slam_inventory_folder(const LLUUID& folder_id, + const LLSD& contents, + LLPointer cb) +{ + { + LL_DEBUGS(LOG_INV) << "using item-by-item calls to slam folder, id " << folder_id + << " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL; + for (LLSD::array_const_iterator it = contents.beginArray(); + it != contents.endArray(); + ++it) + { + const LLSD& item_contents = *it; + LLViewerInventoryItem *item = new LLViewerInventoryItem; + item->fromLLSD(item_contents); + link_inventory_object(folder_id, item, cb); + } + remove_folder_contents(folder_id,false,cb); + } +} + +void remove_folder_contents(const LLUUID& category, bool keep_outfit_links, + LLPointer cb) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(category, cats, items, + LLInventoryModel::EXCLUDE_TRASH); + for (S32 i = 0; i < items.size(); ++i) + { + LLViewerInventoryItem *item = items.at(i); + if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER)) + continue; + if (item->getIsLinkType()) + { + remove_inventory_item(item->getUUID(), cb); + } + } +} + const std::string NEW_LSL_NAME = "New Script"; // *TODO:Translate? (probably not) const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably not) const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not) @@ -1464,320 +1914,11 @@ const std::string& LLViewerInventoryItem::getName() const } #if 0 -/** - * Class to store sorting order of favorites landmarks in a local file. EXT-3985. - * It replaced previously implemented solution to store sort index in landmark's name as a "@" prefix. - * Data are stored in user home directory. - */ -class LLFavoritesOrderStorage : public LLSingleton - , public LLDestroyClass -{ -public: - /** - * Sets sort index for specified with LLUUID favorite landmark - */ - void setSortIndex(const LLUUID& inv_item_id, S32 sort_index); - - /** - * Gets sort index for specified with LLUUID favorite landmark - */ - S32 getSortIndex(const LLUUID& inv_item_id); - void removeSortIndex(const LLUUID& inv_item_id); - - void getSLURL(const LLUUID& asset_id); - - /** - * Implementation of LLDestroyClass. Calls cleanup() instance method. - * - * It is important this callback is called before gInventory is cleaned. - * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), - * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. - * @see cleanup() - */ - static void destroyClass(); - - const static S32 NO_INDEX; -private: - friend class LLSingleton; - LLFavoritesOrderStorage() : mIsDirty(false) { load(); } - ~LLFavoritesOrderStorage() { save(); } - - /** - * Removes sort indexes for items which are not in Favorites bar for now. - */ - void cleanup(); - - const static std::string SORTING_DATA_FILE_NAME; - - void load(); - void save(); - - void saveFavoritesSLURLs(); - - // Remove record of current user's favorites from file on disk. - void removeFavoritesRecordOfUser(); - - void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); - void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); - - typedef std::map sort_index_map_t; - sort_index_map_t mSortIndexes; - - typedef std::map slurls_map_t; - slurls_map_t mSLURLs; - - bool mIsDirty; - - struct IsNotInFavorites - { - IsNotInFavorites(const LLInventoryModel::item_array_t& items) - : mFavoriteItems(items) - { - - } - - /** - * Returns true if specified item is not found among inventory items - */ - bool operator()(const sort_index_map_t::value_type& id_index_pair) const - { - LLPointer item = gInventory.getItem(id_index_pair.first); - if (item.isNull()) return true; - - LLInventoryModel::item_array_t::const_iterator found_it = - std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); - - return found_it == mFavoriteItems.end(); - } - private: - LLInventoryModel::item_array_t mFavoriteItems; - }; - -}; - -const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; -const S32 LLFavoritesOrderStorage::NO_INDEX = -1; - -void LLFavoritesOrderStorage::setSortIndex(const LLUUID& inv_item_id, S32 sort_index) -{ - mSortIndexes[inv_item_id] = sort_index; - mIsDirty = true; -} - -S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) -{ - sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); - if (it != mSortIndexes.end()) - { - return it->second; - } - return NO_INDEX; -} - -void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) -{ - mSortIndexes.erase(inv_item_id); - mIsDirty = true; -} - -void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) -{ - slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); - if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached - - LLLandmark* lm = gLandmarkList.getAsset(asset_id, - boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); - if (lm) - { - onLandmarkLoaded(asset_id, lm); - } -} - -// static -void LLFavoritesOrderStorage::destroyClass() -{ - LLFavoritesOrderStorage::instance().cleanup(); - if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) - { - LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); - } - else - { - LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); - } -} - -void LLFavoritesOrderStorage::load() -{ - // load per-resident sorting information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); - } -} - -void LLFavoritesOrderStorage::saveFavoritesSLURLs() -{ - // Do not change the file if we are not logged in yet. - if (!LLLoginInstance::getInstance()->authSuccess()) return; - - std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); - if (user_dir.empty()) return; - - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - llifstream in_file; - in_file.open(filename); - LLSD fav_llsd; - if (in_file.is_open()) - { - LLSDSerialize::fromXML(fav_llsd, in_file); - } - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLSD user_llsd; - for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) - { - LLSD value; - value["name"] = (*it)->getName(); - value["asset_id"] = (*it)->getAssetUUID(); - - slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); - if (slurl_iter != mSLURLs.end()) - { - value["slurl"] = slurl_iter->second; - user_llsd[(*it)->getSortField()] = value; - } - } - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - fav_llsd[av_name.getLegacyName()] = user_llsd; - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, file); -} - -void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - if (fav_llsd.has(av_name.getLegacyName())) - { - fav_llsd.erase(av_name.getLegacyName()); - } - - llofstream out_file; - out_file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, out_file); - -} - -void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) -{ - if (!landmark) return; - - LLVector3d pos_global; - if (!landmark->getGlobalPos(pos_global)) - { - // If global position was unknown on first getGlobalPos() call - // it should be set for the subsequent calls. - landmark->getGlobalPos(pos_global); - } - - if (!pos_global.isExactlyZero()) - { - LLLandmarkActions::getSLURLfromPosGlobal(pos_global, - boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); - } -} - -void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) -{ - mSLURLs[asset_id] = slurl; -} - -void LLFavoritesOrderStorage::save() -{ - // nothing to save if clean - if (!mIsDirty) return; - - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ or similar. JC - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - LLSD settings_llsd; - - for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } -} - -void LLFavoritesOrderStorage::cleanup() -{ - // nothing to clean - if (!mIsDirty) return; - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - IsNotInFavorites is_not_in_fav(items); - - sort_index_map_t aTempMap; - //copy unremoved values from mSortIndexes to aTempMap - std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), - inserter(aTempMap, aTempMap.begin()), - is_not_in_fav); - - //Swap the contents of mSortIndexes and aTempMap - mSortIndexes.swap(aTempMap); -} - - S32 LLViewerInventoryItem::getSortField() const { return LLFavoritesOrderStorage::instance().getSortIndex(mUUID); } -void LLViewerInventoryItem::setSortField(S32 sortField) -{ - LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); - getSLURL(); -} - void LLViewerInventoryItem::getSLURL() { LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); @@ -1857,7 +1998,7 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const { return LLWearableType::WT_INVALID; } - return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); + return LLWearableType::inventoryFlagsToWearableType(getFlags()); } void LLViewerInventoryItem::setWearableType(LLWearableType::EType type) @@ -1898,7 +2039,7 @@ LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); if (linked_item && linked_item->getIsLinkType()) { - LL_WARNS() << "Warning: Accessing link to link" << LL_ENDL; + LL_WARNS(LOG_INV) << "Warning: Accessing link to link" << LL_ENDL; return NULL; } return linked_item; diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 4fb18eaa6..7b9db7234 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -115,14 +115,13 @@ public: void cloneViewerItem(LLPointer& newitem) const; // virtual methods - virtual void removeFromServer( void ); virtual void updateParentOnServer(BOOL restamp) const; virtual void updateServer(BOOL is_new) const; void fetchFromServer(void) const; - //virtual void packMessage(LLMessageSystem* msg) const; + virtual void packMessage(LLMessageSystem* msg) const; virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); - virtual BOOL unpackMessage(LLSD item); + virtual BOOL unpackMessage(const LLSD& item); virtual BOOL importFile(LLFILE* fp); virtual BOOL importLegacyStream(std::istream& input_stream); @@ -137,7 +136,6 @@ public: void setComplete(BOOL complete) { mIsComplete = complete; } //void updateAssetOnServer() const; - virtual void packMessage(LLMessageSystem* msg) const; virtual void setTransactionID(const LLTransactionID& transaction_id); struct comparePointers { @@ -201,16 +199,17 @@ public: LLViewerInventoryCategory(const LLViewerInventoryCategory* other); void copyViewerCategory(const LLViewerInventoryCategory* other); - virtual void removeFromServer(); virtual void updateParentOnServer(BOOL restamp_children) const; virtual void updateServer(BOOL is_new) const; + virtual void packMessage(LLMessageSystem* msg) const; + const LLUUID& getOwnerID() const { return mOwnerID; } // Version handling enum { VERSION_UNKNOWN = -1, VERSION_INITIAL = 1 }; - S32 getVersion() const { return mVersion; } - void setVersion(S32 version) { mVersion = version; } + S32 getVersion() const; + void setVersion(S32 version); // Returns true if a fetch was issued. bool fetch(); @@ -221,6 +220,8 @@ public: enum { DESCENDENT_COUNT_UNKNOWN = -1 }; S32 getDescendentCount() const { return mDescendentCount; } void setDescendentCount(S32 descendents) { mDescendentCount = descendents; } + // How many descendents do we currently have information for in the InventoryModel? + S32 getViewerDescendentCount() const; // file handling on the viewer. These are not meant for anything // other than caching. @@ -228,6 +229,8 @@ public: bool importFileLocal(LLFILE* fp); void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); + virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + virtual BOOL unpackMessage(const LLSD& category); private: friend class LLInventoryModel; @@ -250,7 +253,7 @@ class LLViewerJointAttachment; //void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp); // [SL:KB] - Patch: Appearance-DnDWear | Checked: 2010-09-28 (Catznip-3.0.0a) | Added: Catznip-2.2.0a -void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace); +void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace = false); // [/SL:KB] void activate_gesture_cb(const LLUUID& inv_item); @@ -270,9 +273,11 @@ private: }; typedef boost::function inventory_func_type; -void no_op_inventory_func(const LLUUID&); // A do-nothing inventory_func - +typedef boost::function llsd_func_type; typedef boost::function nullary_func_type; + +void no_op_inventory_func(const LLUUID&); // A do-nothing inventory_func +void no_op_llsd_func(const LLSD&); // likewise for LLSD void no_op(); // A do-nothing nullary func. // Shim between inventory callback and boost function/callable @@ -280,7 +285,7 @@ class LLBoostFuncInventoryCallback: public LLInventoryCallback { public: - LLBoostFuncInventoryCallback(inventory_func_type fire_func, + LLBoostFuncInventoryCallback(inventory_func_type fire_func = no_op_inventory_func, nullary_func_type destroy_func = no_op): mFireFunc(fire_func), mDestroyFunc(destroy_func) @@ -354,14 +359,16 @@ void copy_inventory_item( const std::string& new_name, LLPointer cb); -void link_inventory_item( - const LLUUID& agent_id, - const LLUUID& item_id, - const LLUUID& parent_id, - const std::string& new_name, - const std::string& new_description, - const LLAssetType::EType asset_type, - LLPointer cb); +// utility functions for inventory linking. +void link_inventory_object(const LLUUID& category, + LLConstPointer baseobj, + LLPointer cb); +void link_inventory_object(const LLUUID& category, + const LLUUID& id, + LLPointer cb); +void link_inventory_array(const LLUUID& category, + LLInventoryObject::const_object_list_t& baseobj_array, + LLPointer cb); void move_inventory_item( const LLUUID& agent_id, @@ -371,6 +378,46 @@ void move_inventory_item( const std::string& new_name, LLPointer cb); +void update_inventory_item( + LLViewerInventoryItem *update_item, + LLPointer cb); + +void update_inventory_item( + const LLUUID& item_id, + const LLSD& updates, + LLPointer cb); + +void update_inventory_category( + const LLUUID& cat_id, + const LLSD& updates, + LLPointer cb); + +void remove_inventory_items( + LLInventoryObject::object_list_t& items, + LLPointer cb); + +void remove_inventory_item( + LLPointer obj, + LLPointer cb, + bool immediate_delete = false); + +void remove_inventory_item( + const LLUUID& item_id, + LLPointer cb, + bool immediate_delete = false); + +void remove_inventory_category( + const LLUUID& cat_id, + LLPointer cb); + +void remove_inventory_object( + const LLUUID& object_id, + LLPointer cb); + +void purge_descendents_of( + const LLUUID& cat_id, + LLPointer cb); + const LLUUID get_folder_by_itemtype(const LLInventoryItem *src); void copy_inventory_from_notecard(const LLUUID& destination_id, @@ -385,4 +432,11 @@ void menu_create_inventory_item(LLFolderView* root, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null); +void slam_inventory_folder(const LLUUID& folder_id, + const LLSD& contents, + LLPointer cb); + +void remove_folder_contents(const LLUUID& folder_id, bool keep_outfit_links, + LLPointer cb); + #endif // LL_LLVIEWERINVENTORY_H diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 618197a51..b63720c6d 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1483,7 +1483,7 @@ void LLViewerMedia::openIDSetup(const std::string &openid_url, const std::string // postRaw() takes ownership of the buffer and releases it later, so we need to allocate a new buffer here. size_t size = openid_token.size(); - char* data = new char[size]; + U8* data = new U8[size]; memcpy(data, openid_token.data(), size); LLHTTPClient::postRaw( diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 09e314969..7d9bb4eed 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -986,7 +986,12 @@ class LLOpenTaskOffer : public LLInventoryAddedObserver protected: /*virtual*/ void done() { - for (uuid_vec_t::iterator it = mAdded.begin(); it != mAdded.end();) + uuid_vec_t added; + for(uuid_set_t::const_iterator it = gInventory.getAddedIDs().begin(); it != gInventory.getAddedIDs().end(); ++it) + { + added.push_back(*it); + } + for (uuid_vec_t::iterator it = added.begin(); it != added.end();) { const LLUUID& item_uuid = *it; bool was_moved = false; @@ -1008,13 +1013,12 @@ protected: if (was_moved) { - it = mAdded.erase(it); + it = added.erase(it); } else ++it; } - open_inventory_offer(mAdded, ""); - mAdded.clear(); + open_inventory_offer(added, ""); } }; @@ -1023,8 +1027,12 @@ class LLOpenTaskGroupOffer : public LLInventoryAddedObserver protected: /*virtual*/ void done() { - open_inventory_offer(mAdded, "group_offer"); - mAdded.clear(); + uuid_vec_t added; + for(uuid_set_t::const_iterator it = gInventory.getAddedIDs().begin(); it != gInventory.getAddedIDs().end(); ++it) + { + added.push_back(*it); + } + open_inventory_offer(added, "group_offer"); gInventory.removeObserver(this); delete this; } @@ -1058,6 +1066,13 @@ void start_new_inventory_observer() gInventoryMoveObserver = new LLViewerInventoryMoveFromWorldObserver; gInventory.addObserver(gInventoryMoveObserver); } + + if (!gNewInventoryHintObserver) + { + // Observer is deleted by gInventory + gNewInventoryHintObserver = new LLNewInventoryHintObserver(); + gInventory.addObserver(gNewInventoryHintObserver); + } } class LLDiscardAgentOffer : public LLInventoryFetchItemsObserver diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 0dc62fb65..0f4f433d5 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -143,6 +143,7 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco { gAgentAvatarp = new LLVOAvatarSelf(id, pcode, regionp); gAgentAvatarp->initInstance(); + gAgentWearables.setAvatarObject(gAgentAvatarp); } else { @@ -2548,8 +2549,8 @@ void LLViewerObject::dirtyInventory() mInventory->clear(); // will deref and delete entries delete mInventory; mInventory = NULL; - mInventoryDirty = TRUE; } + mInventoryDirty = TRUE; } void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data) @@ -2586,12 +2587,15 @@ void LLViewerObject::clearInventoryListeners() void LLViewerObject::requestInventory() { - mInventoryDirty = FALSE; + if(mInventoryDirty && mInventory && !mInventoryCallbacks.empty()) + { + mInventory->clear(); // will deref and delete entries + delete mInventory; + mInventory = NULL; + mInventoryDirty = FALSE; //since we are going to request it now + } if(mInventory) { - //mInventory->clear() // will deref and delete it - //delete mInventory; - //mInventory = NULL; doInventoryCallback(); } // throw away duplicate requests @@ -5897,6 +5901,11 @@ void LLViewerObject::resetChildrenPosition(const LLVector3& offset, BOOL simplif return ; } +// virtual +BOOL LLViewerObject::isTempAttachment() const +{ + return (mID.notNull() && (mID == mAttachmentItemID)); +} // std::string LLViewerObject::getAttachmentPointName() diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 4b498507e..ff5734a19 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -179,6 +179,7 @@ public: virtual BOOL isAttachment() const { return FALSE; } virtual LLVOAvatar* getAvatar() const; //get the avatar this object is attached to, or NULL if object is not an attachment virtual BOOL isHUDAttachment() const { return FALSE; } + virtual BOOL isTempAttachment() const; virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() @@ -824,7 +825,7 @@ public: BOOL getLastUpdateCached() const; void setLastUpdateCached(BOOL last_update_cached); private: - LLUUID mAttachmentItemID; // ItemID when item is in user inventory. + LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory. EObjectUpdateType mLastUpdateType; BOOL mLastUpdateCached; }; diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 6c6179600..1f8add5c2 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -385,7 +385,7 @@ void LLViewerWearable::writeToAvatar(LLAvatarAppearance *avatarp) // Updates the user's avatar's appearance, replacing this wearables' parameters and textures with default values. // static -void LLViewerWearable::removeFromAvatar( LLWearableType::EType type, BOOL upload_bake ) +void LLViewerWearable::removeFromAvatar( LLWearableType::EType type, bool upload_bake ) { if (!isAgentAvatarValid()) return; @@ -507,7 +507,7 @@ void LLViewerWearable::revertValues() { panel->updateScrollingPanelList(); }*/ - if( LLFloaterCustomize::instanceExists() && gAgentWearables.getWearableIndex(this)==0 ) + if (LLFloaterCustomize::instanceExists() && LLFloaterCustomize::getInstance()->getCurrentWearablePanel()->getWearable() == this) LLFloaterCustomize::getInstance()->updateScrollingPanelList(); } @@ -522,7 +522,7 @@ void LLViewerWearable::saveValues() panel->updateScrollingPanelList(); }*/ - if( LLFloaterCustomize::instanceExists() && gAgentWearables.getWearableIndex(this)==0) + if (LLFloaterCustomize::instanceExists() && LLFloaterCustomize::getInstance()->getCurrentWearablePanel()->getWearable() == this) LLFloaterCustomize::getInstance()->updateScrollingPanelList(); } diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index fbf4c87a0..3f5d8ec17 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -63,8 +63,8 @@ public: BOOL isOldVersion() const; /*virtual*/ void writeToAvatar(LLAvatarAppearance *avatarp); - void removeFromAvatar( BOOL upload_bake ) { LLViewerWearable::removeFromAvatar( mType, upload_bake ); } - static void removeFromAvatar( LLWearableType::EType type, BOOL upload_bake ); + void removeFromAvatar( bool upload_bake = false ) { LLViewerWearable::removeFromAvatar( mType, upload_bake ); } + static void removeFromAvatar( LLWearableType::EType type, bool upload_bake = false); /*virtual*/ EImportResult importStream( std::istream& input_stream, LLAvatarAppearance* avatarp ); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 450fbd879..0d7ce6e58 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1078,6 +1078,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mIsEditingAppearance(FALSE), mUseLocalAppearance(FALSE), mUseServerBakes(FALSE), // FIXME DRANO consider using boost::optional, defaulting to unknown. + mLastUpdateRequestCOFVersion(-1), + mLastUpdateReceivedCOFVersion(-1), // mHasPhysicsParameters( false ), mIdleMinute(0), @@ -1144,6 +1146,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mRuthTimer.reset(); mRuthDebugTimer.reset(); mDebugExistenceTimer.reset(); + mLastAppearanceMessageTimer.reset(); mPelvisOffset = LLVector3(0.0f,0.0f,0.0f); mLastPelvisToFoot = 0.0f; mPelvisFixup = 0.0f; @@ -3831,10 +3834,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mUseServerBakes, central_bake_version); std::string origin_string = bakedTextureOriginInfo(); debug_line += " [" + origin_string + "]"; - const LLAppearanceMgr& appmgr(LLAppearanceMgr::instance()); - S32 curr_cof_version = appmgr.getCOFVersion(); - S32 last_request_cof_version = appmgr.getLastUpdateRequestCOFVersion(); - S32 last_received_cof_version = appmgr.getLastAppearanceUpdateCOFVersion(); + S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion(); + S32 last_request_cof_version = mLastUpdateRequestCOFVersion; + S32 last_received_cof_version = mLastUpdateReceivedCOFVersion; if (isSelf()) { debug_line += llformat(" - cof: %d req: %d rcv:%d", @@ -3854,6 +3856,13 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) debug_line += llformat(" %s", (mIsSitting ? "S" : "T")); debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); } + F32 elapsed = mLastAppearanceMessageTimer.getElapsedTimeF32(); + static const char *elapsed_chars = "Xx*..."; + U32 bucket = U32(elapsed*2); + if (bucket < strlen(elapsed_chars)) + { + debug_line += llformat(" %c", elapsed_chars[bucket]); + } addDebugText(debug_line); } if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked")) @@ -6350,7 +6359,7 @@ BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable) //----------------------------------------------------------------------------- // updateSexDependentLayerSets() //----------------------------------------------------------------------------- -void LLVOAvatar::updateSexDependentLayerSets( BOOL upload_bake ) +void LLVOAvatar::updateSexDependentLayerSets( bool upload_bake ) { invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet, upload_bake ); invalidateComposite( mBakedTextureDatas[BAKED_UPPER].mTexLayerSet, upload_bake ); @@ -6958,6 +6967,29 @@ const std::string LLVOAvatar::getAttachedPointName(const LLUUID& inv_item_id) return LLStringUtil::null; } +LLViewerObject * LLVOAvatar::findAttachmentByID( const LLUUID & target_id ) const +{ + for(attachment_map_t::const_iterator attachment_points_iter = mAttachmentPoints.begin(); + attachment_points_iter != gAgentAvatarp->mAttachmentPoints.end(); + ++attachment_points_iter) + { + LLViewerJointAttachment* attachment = attachment_points_iter->second; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject *attached_object = (*attachment_iter); + if (attached_object && + attached_object->getID() == target_id) + { + return attached_object; + } + } + } + + return NULL; +} + // virtual void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset, BOOL upload_result ) { @@ -7936,7 +7968,8 @@ void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix, LLVisualParam* param = getFirstVisualParam(); for (S32 i = 0; i < (S32)params_for_dump.size(); i++) { - while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT + while( param && ((param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) && + (param->getGroup() != VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE)) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT { param = getNextVisualParam(); } @@ -8017,7 +8050,8 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe { for( S32 i = 0; i < num_blocks; i++ ) { - while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT + while( param && ((param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) && + (param->getGroup() != VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE)) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT { param = getNextVisualParam(); } @@ -8038,7 +8072,8 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe } } - const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT + const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE) + + getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT if (num_blocks != expected_tweakable_count) { LL_DEBUGS("Avatar") << "Number of params in AvatarAppearance msg (" << num_blocks << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << LL_ENDL; @@ -8114,6 +8149,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) return; } + mLastAppearanceMessageTimer.reset(); + ESex old_sex = getSex(); LLAppearanceMessageContents contents; @@ -8130,8 +8167,14 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) return; } + if (appearance_version > 1) + { + LL_WARNS() << "unsupported appearance version " << appearance_version << ", discarding appearance message" << LL_ENDL; + return; + } + S32 this_update_cof_version = contents.mCOFVersion; - S32 last_update_request_cof_version = LLAppearanceMgr::instance().mLastUpdateRequestCOFVersion; + S32 last_update_request_cof_version = mLastUpdateRequestCOFVersion; // Only now that we have result of appearance_version can we decide whether to bail out. if( isSelf() ) @@ -8140,8 +8183,6 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) << " last_update_request_cof_version " << last_update_request_cof_version << " my_cof_version " << LLAppearanceMgr::instance().getCOFVersion() << LL_ENDL; - LLAppearanceMgr::instance().setLastAppearanceUpdateCOFVersion(this_update_cof_version); - if (getRegion() && (getRegion()->getCentralBakeVersion()==0)) { LL_WARNS() << avString() << "Received AvatarAppearance message for self in non-server-bake region" << LL_ENDL; @@ -8189,8 +8230,16 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) return; } - setIsUsingServerBakes(appearance_version > 0); + // No backsies zone - if we get here, the message should be valid and usable, will be processed. + setIsUsingServerBakes(appearance_version > 0); + + // Note: + // RequestAgentUpdateAppearanceResponder::onRequestRequested() + // assumes that cof version is only updated with server-bake + // appearance messages. + mLastUpdateReceivedCOFVersion = this_update_cof_version; + applyParsedTEMessage(contents.mTEContents); SHClientTagMgr::instance().updateAvatarTag(this); @@ -8267,7 +8316,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) } } } - const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT + const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE) + + getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT if (num_params != expected_tweakable_count) { LL_DEBUGS("Avatar") << "Number of params in AvatarAppearance msg (" << num_params << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << LL_ENDL; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index aeef51992..8ed1430e6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -654,7 +654,7 @@ private: public: void debugColorizeSubMeshes(U32 i, const LLColor4& color); virtual void updateMeshTextures(); - void updateSexDependentLayerSets(BOOL upload_bake); + void updateSexDependentLayerSets(bool upload_bake = false); virtual void dirtyMesh(); // Dirty the avatar mesh void updateMeshData(); protected: @@ -747,6 +747,8 @@ public: void cleanupAttachedMesh( LLViewerObject* pVO ); static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj); /*virtual*/ BOOL isWearingWearableType(LLWearableType::EType type ) const; + LLViewerObject * findAttachmentByID( const LLUUID & target_id ) const; + protected: LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object); void lazyAttach(); @@ -1034,6 +1036,18 @@ public: protected: LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory. + LLFrameTimer mLastAppearanceMessageTimer; // Time since last appearance message received. + + //-------------------------------------------------------------------- + // COF monitoring + //-------------------------------------------------------------------- + +public: + // COF version of last viewer-initiated appearance update request. For non-self avs, this will remain at default. + S32 mLastUpdateRequestCOFVersion; + + // COF version of last appearance message received for this av. + S32 mLastUpdateReceivedCOFVersion; /** Diagnostics ** ** diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 33ee35dfa..3a3c8d550 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -221,7 +221,7 @@ bool check_for_unsupported_baked_appearance() return true; gAgentAvatarp->checkForUnsupportedServerBakeAppearance(); - return false; + return LLApp::isExiting(); } void force_bake_all_textures() @@ -273,6 +273,7 @@ void LLVOAvatarSelf::initInstance() //doPeriodically(output_self_av_texture_diagnostics, 30.0); doPeriodically(update_avatar_rez_metrics, 5.0); doPeriodically(check_for_unsupported_baked_appearance, 120.0); + doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0); } void LLVOAvatarSelf::setHoverIfRegionEnabled() @@ -283,7 +284,7 @@ void LLVOAvatarSelf::setHoverIfRegionEnabled() if (region->avatarHoverHeightEnabled()) { F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); - setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z))); + setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z, MIN_HOVER_Z, MAX_HOVER_Z))); LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << hover_z << LL_ENDL; } else @@ -297,11 +298,39 @@ void LLVOAvatarSelf::setHoverIfRegionEnabled() LL_INFOS("Avatar") << avString() << " region or simulator features not known, no change on hover" << LL_ENDL; if (region) { - region->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); + region->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived, this, _1)); } } } +bool LLVOAvatarSelf::checkStuckAppearance() +{ + if (!gAgentAvatarp->isUsingServerBakes()) + return false; + const F32 CONDITIONAL_UNSTICK_INTERVAL = 300.0; + const F32 UNCONDITIONAL_UNSTICK_INTERVAL = 600.0; + + if (gAgentWearables.isCOFChangeInProgress()) + { + LL_DEBUGS("Avatar") << "checking for stuck appearance" << LL_ENDL; + F32 change_time = gAgentWearables.getCOFChangeTime(); + LL_DEBUGS("Avatar") << "change in progress for " << change_time << " seconds" << LL_ENDL; + S32 active_hp = LLAppearanceMgr::instance().countActiveHoldingPatterns(); + LL_DEBUGS("Avatar") << "active holding patterns " << active_hp << " seconds" << LL_ENDL; + S32 active_copies = LLAppearanceMgr::instance().getActiveCopyOperations(); + LL_DEBUGS("Avatar") << "active copy operations " << active_copies << LL_ENDL; + + if ((change_time > CONDITIONAL_UNSTICK_INTERVAL && active_copies == 0) || + (change_time > UNCONDITIONAL_UNSTICK_INTERVAL)) + { + gAgentWearables.notifyLoadingFinished(); + } + } + + // Return false to continue running check periodically. + return LLApp::isExiting(); +} + // virtual void LLVOAvatarSelf::markDead() { @@ -733,14 +762,9 @@ void LLVOAvatarSelf::updateVisualParams() LLVOAvatar::updateVisualParams(); } -/*virtual*/ -void LLVOAvatarSelf::idleUpdateAppearanceAnimation() +void LLVOAvatarSelf::writeWearablesToAvatar() { - // Animate all top-level wearable visual parameters - gAgentWearables.animateAllWearableParams(calcMorphAmount(), FALSE); - - // apply wearable visual params to avatar - for (U32 type = 0; type < LLWearableType::WT_COUNT; type++) +for (U32 type = 0; type < LLWearableType::WT_COUNT; type++) { LLWearable *wearable = gAgentWearables.getTopWearable((LLWearableType::EType)type); if (wearable) @@ -749,6 +773,17 @@ void LLVOAvatarSelf::idleUpdateAppearanceAnimation() } } +} + +/*virtual*/ +void LLVOAvatarSelf::idleUpdateAppearanceAnimation() +{ + // Animate all top-level wearable visual parameters + gAgentWearables.animateAllWearableParams(calcMorphAmount(), FALSE); + + // Apply wearable visual params to avatar + writeWearablesToAvatar(); + //allow avatar to process updates LLVOAvatar::idleUpdateAppearanceAnimation(); @@ -779,12 +814,6 @@ void LLVOAvatarSelf::stopMotionFromSource(const LLUUID& source_id) } } -//virtual -U32 LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys, void **user_data, U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp) -{ - return LLVOAvatar::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); -} - void LLVOAvatarSelf::setLocalTextureTE(U8 te, LLViewerTexture* image, U32 index) { if (te >= TEX_NUM_INDICES) @@ -1630,8 +1659,8 @@ BOOL LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex t return LLVOAvatar::isTextureVisible(type); } - U32 index = gAgentWearables.getWearableIndex(wearable); - return isTextureVisible(type,index); + U32 index; + return gAgentWearables.getWearableIndex(wearable, index) && isTextureVisible(type, index); } @@ -3014,6 +3043,11 @@ void LLVOAvatarSelf::onCustomizeStart(bool disable_camera_switch) { if (isAgentAvatarValid()) { + if (!gAgentAvatarp->mEndCustomizeCallback.get()) + { + gAgentAvatarp->mEndCustomizeCallback = new LLUpdateAppearanceOnDestroy; + } + gAgentAvatarp->mIsEditingAppearance = true; gAgentAvatarp->mUseLocalAppearance = true; @@ -3054,10 +3088,10 @@ void LLVOAvatarSelf::onCustomizeEnd(bool disable_camera_switch) gAgentCamera.resetView(); } - if (gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion()) - { - LLAppearanceMgr::instance().requestServerAppearanceUpdate(); - } + // Dereferencing the previous callback will cause + // updateAppearanceFromCOF to be called, whenever all refs + // have resolved. + gAgentAvatarp->mEndCustomizeCallback = NULL; } } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 36c4d1084..a83a6d029 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -32,6 +32,7 @@ #include "llvoavatar.h" struct LocalTextureData; +class LLInventoryCallback; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -102,17 +103,12 @@ public: /*virtual*/ BOOL setVisualParamWeight(const char* param_name, F32 weight, bool upload_bake = false ); /*virtual*/ BOOL setVisualParamWeight(S32 index, F32 weight, bool upload_bake = false ); /*virtual*/ void updateVisualParams(); + void writeWearablesToAvatar(); /*virtual*/ void idleUpdateAppearanceAnimation(); - /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys, - void **user_data, - U32 block_num, - const EObjectUpdateType update_type, - LLDataPacker *dp); - private: // helper function. Passed in param is assumed to be in avatar's parameter list. - BOOL setParamWeight(const LLViewerVisualParam *param, F32 weight, bool upload_bake = FALSE ); + BOOL setParamWeight(const LLViewerVisualParam *param, F32 weight, bool upload_bake = false ); @@ -140,6 +136,7 @@ public: public: /*virtual*/ BOOL updateCharacter(LLAgent &agent); /*virtual*/ void idleUpdateTractorBeam(); + bool checkStuckAppearance(); //-------------------------------------------------------------------- // Loading state @@ -352,6 +349,7 @@ private: public: static void onCustomizeStart(bool disable_camera_switch = false); static void onCustomizeEnd(bool disable_camera_switch = false); + LLPointer mEndCustomizeCallback; //-------------------------------------------------------------------- // Visibility diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 20abb1fb9..aa3acc14a 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -290,7 +290,7 @@ void LLWebProfile::post(LLPointer image, const LLSD& config, c size_t size = body_size + image->getDataSize() + footer_size; // postRaw() takes ownership of the buffer and releases it later. - char* data = new char [size]; + U8* data = new U8 [size]; memcpy(data, body.str().data(), body_size); // Insert the image data. memcpy(data + body_size, image->getData(), image->getDataSize()); diff --git a/indra/newview/rlvinventory.cpp b/indra/newview/rlvinventory.cpp index 5c99c5ec9..ccfa1f3f7 100644 --- a/indra/newview/rlvinventory.cpp +++ b/indra/newview/rlvinventory.cpp @@ -406,7 +406,7 @@ void RlvRenameOnWearObserver::doneIdle() if (gInventory.isObjectDescendentOf(idAttachItem, pRlvRoot->getUUID())) items.push_back(gInventory.getItem(idAttachItem)); else - items = gInventory.collectLinkedItems(idAttachItem, pRlvRoot->getUUID()); + items = gInventory.collectLinksTo(idAttachItem, pRlvRoot->getUUID()); if (items.empty()) continue; @@ -462,12 +462,12 @@ void RlvRenameOnWearObserver::doneIdle() else { // "No modify" item with a non-renameable parent: create a new folder named and move the item into it - LLUUID idFolder = gInventory.createNewCategory(pFolder->getUUID(), LLFolderType::FT_NONE, strFolderName, - &RlvRenameOnWearObserver::onCategoryCreate, new LLUUID(pItem->getUUID())); + inventory_func_type func = boost::bind(&RlvRenameOnWearObserver::onCategoryCreate, this, _1, pItem->getUUID()); + LLUUID idFolder = gInventory.createNewCategory(pFolder->getUUID(), LLFolderType::FT_NONE, strFolderName, func); if (idFolder.notNull()) { // Not using the new 'CreateInventoryCategory' cap so manually invoke the callback - RlvRenameOnWearObserver::onCategoryCreate(LLSD().with("folder_id", idFolder), new LLUUID(pItem->getUUID())); + RlvRenameOnWearObserver::onCategoryCreate(idFolder, pItem->getUUID()); } } } @@ -480,15 +480,10 @@ void RlvRenameOnWearObserver::doneIdle() } // Checked: 2012-03-22 (RLVa-1.4.6) | Added: RLVa-1.4.6 -void RlvRenameOnWearObserver::onCategoryCreate(const LLSD& sdData, void* pParam) +void RlvRenameOnWearObserver::onCategoryCreate(const LLUUID& folder_id, const LLUUID& item_id) { - LLUUID idFolder = sdData["folder_id"].asUUID(); - LLUUID* pidItem = (LLUUID*)pParam; - - if ( (idFolder.notNull()) && (pidItem) && (pidItem->notNull()) ) - move_inventory_item(gAgent.getID(), gAgent.getSessionID(), *pidItem, idFolder, std::string(), NULL); - - delete pidItem; + if (folder_id.notNull() && item_id.notNull()) + move_inventory_item(gAgent.getID(), gAgent.getSessionID(), item_id, folder_id, std::string(), NULL); } // ============================================================================ @@ -516,13 +511,14 @@ bool RlvGiveToRLVOffer::createDestinationFolder(const std::string& strPath) const LLUUID& idRlvRoot = RlvInventory::instance().getSharedRootID(); if (idRlvRoot.notNull()) { - onCategoryCreateCallback(LLSD().with("folder_id", idRlvRoot), this); + onCategoryCreate(idRlvRoot); } else { - const LLUUID idTemp = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, RLV_ROOT_FOLDER, onCategoryCreateCallback, (void*)this); + inventory_func_type func = boost::bind(&RlvGiveToRLVOffer::onCategoryCreate, this, _1); + const LLUUID idTemp = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, RLV_ROOT_FOLDER, func); if (idTemp.notNull()) - onCategoryCreateCallback(LLSD().with("folder_id", idTemp), this); + onCategoryCreate(idTemp); } return true; } @@ -532,40 +528,39 @@ bool RlvGiveToRLVOffer::createDestinationFolder(const std::string& strPath) } // Checked: 2014-01-07 (RLVa-1.4.10) -void RlvGiveToRLVOffer::onCategoryCreateCallback(const LLSD& sdData, void* pInstance) +void RlvGiveToRLVOffer::onCategoryCreate(const LLUUID& folder_id) { - RlvGiveToRLVOffer* pThis = (RlvGiveToRLVOffer*)pInstance; - - LLUUID idFolder = sdData["folder_id"].asUUID(); - if (idFolder.isNull()) + if (folder_id.isNull()) { // Problem encountered, abort move - pThis->onDestinationCreated(LLUUID::null, LLStringUtil::null); + onDestinationCreated(LLUUID::null, LLStringUtil::null); return; } - while (pThis->m_DestPath.size() > 1) + LLUUID target_folder = folder_id; + while (m_DestPath.size() > 1) { - std::string strFolder = pThis->m_DestPath.front(); - pThis->m_DestPath.pop_front(); + std::string strFolder = m_DestPath.front(); + m_DestPath.pop_front(); - const LLViewerInventoryCategory* pFolder = RlvInventory::instance().getSharedFolder(idFolder, strFolder, false); + const LLViewerInventoryCategory* pFolder = RlvInventory::instance().getSharedFolder(folder_id, strFolder, false); if (pFolder) { - idFolder = pFolder->getUUID(); + target_folder = pFolder->getUUID(); } else { LLInventoryObject::correctInventoryName(strFolder); - const LLUUID idTemp = gInventory.createNewCategory(idFolder, LLFolderType::FT_NONE, strFolder, onCategoryCreateCallback, pInstance); + inventory_func_type func = boost::bind(&RlvGiveToRLVOffer::onCategoryCreate, this, _1); + const LLUUID idTemp = gInventory.createNewCategory(folder_id, LLFolderType::FT_NONE, strFolder, func); if (idTemp.notNull()) - onCategoryCreateCallback(LLSD().with("folder_id", idTemp), pInstance); + onCategoryCreate(idTemp); return; } } // Destination folder should exist at this point (we'll be deallocated when the function returns) - pThis->onDestinationCreated(idFolder, pThis->m_DestPath.front()); + onDestinationCreated(target_folder, m_DestPath.front()); } // Checked: 2014-01-07 (RLVa-1.4.10) diff --git a/indra/newview/rlvinventory.h b/indra/newview/rlvinventory.h index 0e1d06155..682461857 100644 --- a/indra/newview/rlvinventory.h +++ b/indra/newview/rlvinventory.h @@ -112,7 +112,7 @@ public: virtual void done(); protected: void doneIdle(); - static void onCategoryCreate(const LLSD& sdData, void* pParam); + void onCategoryCreate(const LLUUID& folder_id, const LLUUID& item_id); }; // ============================================================================ @@ -129,7 +129,7 @@ protected: virtual void onDestinationCreated(const LLUUID& idFolder, const std::string& strName) = 0; void moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName); private: - static void onCategoryCreateCallback(const LLSD& sdData, void* pInstance); + void onCategoryCreate(const LLUUID& folder_id); private: std::list m_DestPath; diff --git a/indra/newview/rlvlocks.cpp b/indra/newview/rlvlocks.cpp index 9835a0b71..a2216e0d1 100644 --- a/indra/newview/rlvlocks.cpp +++ b/indra/newview/rlvlocks.cpp @@ -1165,7 +1165,7 @@ bool RlvFolderLocks::getLockedItems(const LLUUID& idFolder, LLInventoryModel::it if (!fItemLocked) { LLInventoryModel::item_array_t itemLinks = - gInventory.collectLinkedItems(pItem->getUUID(), RlvInventory::instance().getSharedRootID()); + gInventory.collectLinksTo(pItem->getUUID(), RlvInventory::instance().getSharedRootID()); for (LLInventoryModel::item_array_t::iterator itItemLink = itemLinks.begin(); (itItemLink < itemLinks.end()) && (!fItemLocked); ++itItemLink) { diff --git a/indra/newview/rlvlocks.h b/indra/newview/rlvlocks.h index 31180aad9..83316097e 100644 --- a/indra/newview/rlvlocks.h +++ b/indra/newview/rlvlocks.h @@ -533,7 +533,7 @@ inline ERlvWearMask RlvWearableLocks::canWear(LLWearableType::EType eType) const return (!isLockedWearableType(eType, RLV_LOCK_ADD)) ? ((!hasLockedWearable(eType)) ? RLV_WEAR - : (gAgentWearables.getWearableCount(eType) < LLAgentWearables::MAX_CLOTHING_PER_TYPE) ? RLV_WEAR_ADD : RLV_WEAR_LOCKED) + : (gAgentWearables.canAddWearable(eType)) ? RLV_WEAR_ADD : RLV_WEAR_LOCKED) : RLV_WEAR_LOCKED; } diff --git a/indra/newview/skins/default/xui/en-us/menu_inventory.xml b/indra/newview/skins/default/xui/en-us/menu_inventory.xml index 1c8967a4a..6811527c8 100644 --- a/indra/newview/skins/default/xui/en-us/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en-us/menu_inventory.xml @@ -260,7 +260,7 @@ - +