diff options
Diffstat (limited to 'indra/newview/llinventorymodel.cpp')
-rw-r--r-- | indra/newview/llinventorymodel.cpp | 2387 |
1 files changed, 718 insertions, 1669 deletions
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 9177d51d5c..53835f0166 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -2,92 +2,66 @@ * @file llinventorymodel.cpp * @brief Implementation of the inventory model used to track agent inventory. * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 "llinventorymodel.h" -#include "llassetstorage.h" -#include "llcrc.h" -#include "lldir.h" -#include "llsys.h" -#include "llxfermanager.h" -#include "message.h" - #include "llagent.h" #include "llagentwearables.h" -#include "llfloater.h" -#include "llfocusmgr.h" +#include "llappearancemgr.h" +#include "llinventorypanel.h" #include "llinventorybridge.h" -#include "llinventoryview.h" -#include "llviewerinventory.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llinventorypanel.h" +#include "llnotificationsutil.h" +#include "llwindow.h" +#include "llviewercontrol.h" +#include "llpreview.h" #include "llviewermessage.h" +#include "llviewerfoldertype.h" #include "llviewerwindow.h" -#include "llviewerregion.h" #include "llappviewer.h" -#include "lldbstrings.h" -#include "llviewerstats.h" -#include "llmutelist.h" -#include "llnotifications.h" +#include "llviewerregion.h" #include "llcallbacklist.h" -#include "llpreview.h" -#include "llviewercontrol.h" #include "llvoavatarself.h" -#include "llsdutil.h" -#include <deque> //#define DIFF_INVENTORY_FILES #ifdef DIFF_INVENTORY_FILES #include "process.h" #endif -BOOL LLInventoryModel::sBackgroundFetchActive = FALSE; -BOOL LLInventoryModel::sAllFoldersFetched = FALSE; -BOOL LLInventoryModel::sFullFetchStarted = FALSE; -S32 LLInventoryModel::sNumFetchRetries = 0; -F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f; -F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f; -BOOL LLInventoryModel::sTimelyFetchPending = FALSE; -LLFrameTimer LLInventoryModel::sFetchTimer; -S16 LLInventoryModel::sBulkFetchCount = 0; - -// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue -static std::deque<LLUUID> sFetchQueue; +// Increment this if the inventory contents change in a non-backwards-compatible way. +// For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect. +const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; +BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- //BOOL decompress_file(const char* src_filename, const char* dst_filename); -const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; -const S32 MAX_FETCH_RETRIES = 10; const char CACHE_FORMAT_STRING[] = "%s.inv"; struct InventoryIDPtrLess @@ -155,9 +129,21 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) LLInventoryModel gInventory; // Default constructor -LLInventoryModel::LLInventoryModel() : - mModifyMask(LLInventoryObserver::ALL), +LLInventoryModel::LLInventoryModel() +: mModifyMask(LLInventoryObserver::ALL), + mChangedItemIDs(), + mCategoryMap(), + mItemMap(), + mCategoryLock(), + mItemLock(), mLastItem(NULL), + mParentChildCategoryTree(), + mParentChildItemTree(), + mObservers(), + mRootFolderID(), + mLibraryRootFolderID(), + mLibraryOwnerID(), + mIsNotifyObservers(FALSE), mIsAgentInvUsable(false) { } @@ -165,20 +151,31 @@ LLInventoryModel::LLInventoryModel() : // Destroys the object LLInventoryModel::~LLInventoryModel() { + cleanupInventory(); +} + +void LLInventoryModel::cleanupInventory() +{ empty(); - for (observer_list_t::iterator iter = mObservers.begin(); - iter != mObservers.end(); ++iter) + // Deleting one observer might erase others from the list, so always pop off the front + while (!mObservers.empty()) { - delete *iter; + observer_list_t::iterator iter = mObservers.begin(); + LLInventoryObserver* observer = *iter; + mObservers.erase(iter); + delete observer; } + mObservers.clear(); } // This is a convenience function to check if one object has a parent // chain up to the category specified by UUID. BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id, - const LLUUID& cat_id) + const LLUUID& cat_id) const { - LLInventoryObject* obj = getObject(obj_id); + if (obj_id == cat_id) return TRUE; + + const LLInventoryObject* obj = getObject(obj_id); while(obj) { const LLUUID& parent_id = obj->getParentUUID(); @@ -197,6 +194,29 @@ BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id, return FALSE; } +const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(const LLUUID& obj_id) const +{ + const LLInventoryObject* obj = getObject(obj_id); + + // Search up the parent chain until we get to root or an acceptable folder. + // This assumes there are no cycles in the tree else we'll get a hang. + LLUUID parent_id = obj->getParentUUID(); + while (!parent_id.isNull()) + { + const LLViewerInventoryCategory *cat = getCategory(parent_id); + if (!cat) break; + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (folder_type != LLFolderType::FT_NONE && + folder_type != LLFolderType::FT_ROOT_INVENTORY && + !LLFolderType::lookupIsEnsembleType(folder_type)) + { + return cat; + } + parent_id = cat->getParentUUID(); + } + return NULL; +} + // Get the object by id. Returns NULL if not found. LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const { @@ -268,6 +288,30 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, items = get_ptr_in_map(mParentChildItemTree, cat_id); } +LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + getDirectDescendentsOf(cat_id,cat_array,item_array); + LLMD5 item_name_hash; + if (!item_array) + { + item_name_hash.finalize(); + return item_name_hash; + } + for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin(); + iter != item_array->end(); + iter++) + { + const LLViewerInventoryItem *item = (*iter); + if (!item) + continue; + item_name_hash.update(item->getName()); + } + item_name_hash.finalize(); + return item_name_hash; +} + // SJB: Added version to lock the arrays to catch potential logic bugs void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id, cat_array_t*& categories, @@ -294,30 +338,18 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) // 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. -LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t, bool create_folder) +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder, + bool find_in_library) { - LLUUID rv = findCatUUID(t); - if(rv.isNull() && isInventoryUsable() && create_folder) - { - LLUUID root_id = gAgent.getInventoryRootID(); - if(root_id.notNull()) - { - rv = createNewCategory(root_id, t, LLStringUtil::null); - } - } - return rv; -} - -// Internal method which looks for a category with the specified -// preferred type. Returns LLUUID::null if not found. -LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type) -{ - LLUUID root_id = gAgent.getInventoryRootID(); - if(LLAssetType::AT_CATEGORY == preferred_type) + LLUUID rv = LLUUID::null; + + const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); + if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { - return root_id; + rv = root_id; } - if(root_id.notNull()) + else if (root_id.notNull()) { cat_array_t* cats = NULL; cats = get_ptr_in_map(mParentChildCategoryTree, root_id); @@ -328,12 +360,21 @@ LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type) { if(cats->get(i)->getPreferredType() == preferred_type) { - return cats->get(i)->getUUID(); + rv = cats->get(i)->getUUID(); + break; } } } } - return LLUUID::null; + + if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + { + if(root_id.notNull()) + { + return createNewCategory(root_id, preferred_type, LLStringUtil::null); + } + } + return rv; } // Convenience function to create a new category. You could call @@ -341,7 +382,7 @@ LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type) // version will take care of details like what the name should be // based on preferred type. Returns the UUID of the new category. LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, - LLAssetType::EType preferred_type, + LLFolderType::EType preferred_type, const std::string& pname) { LLUUID id; @@ -351,9 +392,9 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return id; } - if(preferred_type == LLAssetType::AT_SIMSTATE) + if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { - lldebugs << "Attempt to create simstate category." << llendl; + lldebugs << "Attempt to create undefined category." << llendl; return id; } @@ -365,7 +406,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, } else { - name.assign(LLAssetType::lookupCategoryName(preferred_type)); + name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } // Add the category to the internal representation @@ -423,12 +464,13 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, cat_array_t& cats, item_array_t& items, BOOL include_trash, - LLInventoryCollectFunctor& add) + LLInventoryCollectFunctor& add, + BOOL follow_folder_links) { // Start with categories if(!include_trash) { - LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH)); + const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH); if(trash_id.notNull() && (trash_id == id)) return; } @@ -447,9 +489,40 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, } } - // Move onto items LLViewerInventoryItem* item = NULL; item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id); + + // 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. + if (follow_folder_links && item_array) + { + S32 count = item_array->count(); + for(S32 i = 0; i < count; ++i) + { + item = item_array->get(i); + if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER) + { + LLViewerInventoryCategory *linked_cat = item->getLinkedCategory(); + if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT) + // BAP - was + // LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType())) + // Change back once ensemble typing is in place. + { + if(add(linked_cat,NULL)) + { + // BAP should this be added here? May not + // matter if it's only being used in current + // outfit traversal. + cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat)); + } + collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE); + } + } + } + } + + // Move onto items if(item_array) { S32 count = item_array->count(); @@ -464,31 +537,77 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, } } -// Generates a string containing the path to the item specified by -// item_id. -void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) +void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask) { - std::string temp; - LLInventoryObject* obj = getObject(id); - LLUUID parent_id; - if(obj) parent_id = obj->getParentUUID(); - std::string forward_slash("/"); - while(obj) + const LLInventoryObject *obj = getObject(object_id); + 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()) { - obj = getCategory(parent_id); - if(obj) - { - temp.assign(forward_slash + obj->getName() + temp); - parent_id = obj->getParentUUID(); - } + return; } - path.append(temp); + 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()); + }; + + for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + LLViewerInventoryItem *linked_item = (*iter); + addChangedMask(mask, linked_item->getUUID()); + }; } -bool LLInventoryModel::isInventoryUsable() +const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const +{ + const LLInventoryItem *item = gInventory.getItem(object_id); + if (!item) + { + return object_id; + } + + // Find the base item in case this a link (if it's not a link, + // this will just be inv_item_id) + return item->getLinkedUUID(); +} + +LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const +{ + return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL; +} + +LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id, + const LLUUID& start_folder_id) +{ + 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); + return items; +} + +bool LLInventoryModel::isInventoryUsable() const { bool result = false; - if(gAgent.getInventoryRootID().notNull() && mIsAgentInvUsable) + if(gInventory.getRootFolderID().notNull() && mIsAgentInvUsable) { result = true; } @@ -513,11 +632,14 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) } LLViewerInventoryItem* old_item = getItem(item->getUUID()); + LLPointer<LLViewerInventoryItem> new_item; if(old_item) { // We already have an old item, modify its values + new_item = old_item; LLUUID old_parent_id = old_item->getParentUUID(); LLUUID new_parent_id = item->getParentUUID(); + if(old_parent_id != new_parent_id) { // need to update the parent-child tree @@ -544,12 +666,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) else { // Simply add this item - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item = new LLViewerInventoryItem(item); addItem(new_item); if(item->getParentUUID().isNull()) { - LLUUID category_id = findCategoryUUIDForType(new_item->getType()); + const LLUUID category_id = findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(new_item->getType())); new_item->setParent(category_id); item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id); if( item_array ) @@ -573,7 +695,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) LLUUID parent_id = item->getParentUUID(); if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID) { - parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); + parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); new_item->setParent(parent_id); } item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id); @@ -586,7 +708,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) // Whoops! No such parent, make one. llinfos << "Lost item: " << new_item->getUUID() << " - " << new_item->getName() << llendl; - parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); + parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); new_item->setParent(parent_id); item_array = get_ptr_in_map(mParentChildItemTree, parent_id); if(item_array) @@ -604,11 +726,40 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) } mask |= LLInventoryObserver::ADD; } - if(item->getType() == LLAssetType::AT_CALLINGCARD) + if(new_item->getType() == LLAssetType::AT_CALLINGCARD) { mask |= LLInventoryObserver::CALLING_CARD; + // Handle user created calling cards. + // Target ID is stored in the description field of the card. + LLUUID id; + std::string desc = new_item->getDescription(); + BOOL isId = desc.empty() ? FALSE : id.set(desc, FALSE); + if (isId) + { + // Valid UUID; set the item UUID and rename it + new_item->setCreator(id); + std::string avatar_name; + + if (gCacheName->getFullName(id, avatar_name)) + { + new_item->rename(avatar_name); + mask |= LLInventoryObserver::LABEL; + } + else + { + // Fetch the current name + gCacheName->get(id, FALSE, + boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), + _1, _2, _3)); + } + + } + } + else if (new_item->getType() == LLAssetType::AT_GESTURE) + { + mask |= LLInventoryObserver::GESTURE; } - addChangedMask(mask, item->getUUID()); + addChangedMask(mask, new_item->getUUID()); return mask; } @@ -749,43 +900,80 @@ void LLInventoryModel::deleteObject(const LLUUID& id) { lldebugs << "LLInventoryModel::deleteObject()" << llendl; LLPointer<LLInventoryObject> obj = getObject(id); + if (!obj) + { + llwarns << "Deleting non-existent object [ id: " << id << " ] " << llendl; + return; + } + + lldebugs << "Deleting inventory object " << id << llendl; + mLastItem = NULL; + LLUUID parent_id = obj->getParentUUID(); + mCategoryMap.erase(id); + mItemMap.erase(id); + //mInventory.erase(id); + item_array_t* item_list = getUnlockedItemArray(parent_id); + if(item_list) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); + item_list->removeObj(item); + } + cat_array_t* cat_list = getUnlockedCatArray(parent_id); + if(cat_list) + { + LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); + cat_list->removeObj(cat); + } + item_list = getUnlockedItemArray(id); + if(item_list) + { + delete item_list; + mParentChildItemTree.erase(id); + } + cat_list = getUnlockedCatArray(id); + if(cat_list) + { + 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) +{ + lldebugs << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << llendl; + LLPointer<LLInventoryObject> obj = getObject(id); if(obj) { - lldebugs << "Deleting inventory object " << id << llendl; - mLastItem = NULL; - LLUUID parent_id = obj->getParentUUID(); - mCategoryMap.erase(id); - mItemMap.erase(id); - //mInventory.erase(id); - item_array_t* item_list = getUnlockedItemArray(parent_id); - if(item_list) - { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); - item_list->removeObj(item); - } - cat_array_t* cat_list = getUnlockedCatArray(parent_id); - if(cat_list) - { - LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); - cat_list->removeObj(cat); - } - item_list = getUnlockedItemArray(id); - if(item_list) - { - delete item_list; - mParentChildItemTree.erase(id); - } - cat_list = getUnlockedCatArray(id); - if(cat_list) - { - delete cat_list; - mParentChildCategoryTree.erase(id); - } - addChangedMask(LLInventoryObserver::REMOVE, id); - obj = NULL; // delete obj + obj->removeFromServer(); + LLPreview::hide(id); + deleteObject(id); } } +void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id) +{ + LLInventoryModel::item_array_t item_array = collectLinkedItems(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++) + { + 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 @@ -848,98 +1036,6 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) } } -void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids, - LLDynamicArray<LLUUID>& item_ids) -{ - // Store off tre UUIDS of parents which are being deleted (thus no - // need to increment) and the parents which are being modified. We - // have to increment the version of the parent with each message - // sent upstream since the dataserver will increment each unique - // parent per update message. - std::set<LLUUID> ignore_parents; - update_map_t inc_parents; - - S32 i; - S32 count = category_ids.count(); - BOOL start_new_message = TRUE; - LLMessageSystem* msg = gMessageSystem; - LLPointer<LLViewerInventoryCategory> cat; - for(i = 0; i < count; i++) - { - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_RemoveInventoryObjects); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - LLUUID cat_id = category_ids.get(i); - - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, cat_id); - cat = getCategory(cat_id); - ignore_parents.insert(cat_id); - addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id); - if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end())) - { - --inc_parents[cat->getParentUUID()]; - } - if(msg->isSendFullFast(_PREHASH_FolderData)) - { - start_new_message = TRUE; - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null); - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - inc_parents.clear(); - } - } - - count = item_ids.count(); - std::set<LLUUID>::iterator not_ignored = ignore_parents.end(); - LLPointer<LLViewerInventoryItem> item; - if((0 == count) && (!start_new_message)) - { - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null); - } - for(i = 0; i < count; i++) - { - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_RemoveInventoryObjects); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null); - } - LLUUID item_id = item_ids.get(i); - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - item = getItem(item_id); - addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id); - if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored)) - { - --inc_parents[item->getParentUUID()]; - } - if(msg->isSendFullFast(_PREHASH_ItemData)) - { - start_new_message = TRUE; - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - inc_parents.clear(); - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - } -} - // Add/remove an observer. If the observer is destroyed, be sure to // remove it. void LLInventoryModel::addObserver(LLInventoryObserver* observer) @@ -952,17 +1048,34 @@ void LLInventoryModel::removeObserver(LLInventoryObserver* observer) mObservers.erase(observer); } -BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) +BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const { return mObservers.find(observer) != mObservers.end(); } -// Call this method when it's time to update everyone on a new state, -// by default, the inventory model will not update observers -// automatically. +void LLInventoryModel::idleNotifyObservers() +{ + if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0)) + { + return; + } + notifyObservers(""); +} + +// Call this method when it's time to update everyone on a new state. // The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] void LLInventoryModel::notifyObservers(const std::string service_name) { + if (mIsNotifyObservers) + { + // Within notifyObservers, something called notifyObservers + // again. This type of recursion is unsafe because it causes items to be + // processed twice, and this can easily lead to infinite loops. + llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl; + return; + } + + mIsNotifyObservers = TRUE; for (observer_list_t::iterator iter = mObservers.begin(); iter != mObservers.end(); ) { @@ -978,48 +1091,42 @@ void LLInventoryModel::notifyObservers(const std::string service_name) observer->changed(mModifyMask); } - // safe way to incrament since changed may delete entries! (@!##%@!@&*!) + // safe way to increment since changed may delete entries! (@!##%@!@&*!) iter = mObservers.upper_bound(observer); } mModifyMask = LLInventoryObserver::NONE; mChangedItemIDs.clear(); + mIsNotifyObservers = FALSE; } // store flag for change // and id of object change applies to void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) { + if (mIsNotifyObservers) + { + // Something marked an item for change within a call to notifyObservers + // (which is in the process of processing the list of items marked for change). + // This means the change may fail to be processed. + llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl; + } + mModifyMask |= mask; if (referent.notNull()) { mChangedItemIDs.insert(referent); } -} - -// This method to prepares a set of mock inventory which provides -// minimal functionality before the actual arrival of inventory. -/* -void LLInventoryModel::mock(const LLUUID& root_id) -{ - llinfos << "LLInventoryModel::mock() " << root_id << llendl; - if(root_id.isNull()) + + // 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) { - llwarns << "Not a valid root id" << llendl; - return; + addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); } - LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory( - root_id, - LLUUID::null, - LLAssetType::AT_CATEGORY, - LLAssetType::lookupCategoryName(LLAssetType::AT_ROOT_CATEGORY), - gAgent.getID()); - addCategory(cat); - gInventory.buildParentChildMap(); } -*/ -//If we get back a normal response, handle it here +// If we get back a normal response, handle it here void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) { start_new_inventory_observer(); @@ -1092,14 +1199,19 @@ void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::str gInventory.notifyObservers("fetchinventory"); } -void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) +bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const { + if(folder_id.isNull()) + { + llwarns << "Calling fetch descendents on NULL folder id!" << llendl; + return false; + } LLViewerInventoryCategory* cat = getCategory(folder_id); if(!cat) { llwarns << "Asked to fetch descendents of non-existent folder: " << folder_id << llendl; - return; + return false; } //S32 known_descendents = 0; ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id); @@ -1112,490 +1224,7 @@ void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) //{ // known_descendents += items->count(); //} - if(!cat->fetchDescendents()) - { - //llinfos << "Not fetching descendents" << llendl; - } -} - -//Initialize statics. -bool LLInventoryModel::isBulkFetchProcessingComplete() -{ - return ( (sFetchQueue.empty() - && sBulkFetchCount<=0) ? TRUE : FALSE ) ; -} - -class fetchDescendentsResponder: public LLHTTPClient::Responder -{ - public: - fetchDescendentsResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; - //fetchDescendentsResponder() {}; - void result(const LLSD& content); - void error(U32 status, const std::string& reason); - public: - typedef std::vector<LLViewerInventoryCategory*> folder_ref_t; - protected: - LLSD mRequestSD; -}; - -//If we get back a normal response, handle it here -void fetchDescendentsResponder::result(const LLSD& content) -{ - if (content.has("folders")) - { - - for(LLSD::array_const_iterator folder_it = content["folders"].beginArray(); - folder_it != content["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. - //{ - // llwarns << "Got a UpdateInventoryItem for the wrong agent." - // << llendl; - // 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<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id); - - if (parent_id.isNull()) - { - LLPointer<LLViewerInventoryItem> 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(LLAssetType::AT_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("fetchDescendents"); - - } - } - } - - 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); - - if (LLInventoryModel::sFullFetchStarted) - { - sFetchQueue.push_back(tcategory->getUUID()); - } - else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) ) - { - gInventory.updateCategory(tcategory); - } - - } - LLPointer<LLViewerInventoryItem> 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); - } - - } - } - - if (content.has("bad_folders")) - { - for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray(); - folder_it != content["bad_folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - - //These folders failed on the dataserver. We probably don't want to retry them. - llinfos << "Folder " << folder_sd["folder_id"].asString() - << "Error: " << folder_sd["error"].asString() << llendl; - } - } - - LLInventoryModel::incrBulkFetch(-1); - - if (LLInventoryModel::isBulkFetchProcessingComplete()) - { - llinfos << "Inventory fetch completed" << llendl; - if (LLInventoryModel::sFullFetchStarted) - { - LLInventoryModel::sAllFoldersFetched = TRUE; - } - LLInventoryModel::stopBackgroundFetch(); - } - - gInventory.notifyObservers("fetchDescendents"); -} - -//If we get back an error (not found, etc...), handle it here -void fetchDescendentsResponder::error(U32 status, const std::string& reason) -{ - llinfos << "fetchDescendentsResponder::error " - << status << ": " << reason << llendl; - - LLInventoryModel::incrBulkFetch(-1); - - if (status==499) //timed out. Let's be awesome! - { - 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"]; - sFetchQueue.push_front(folder_id); - } - } - else - { - if (LLInventoryModel::isBulkFetchProcessingComplete()) - { - if (LLInventoryModel::sFullFetchStarted) - { - LLInventoryModel::sAllFoldersFetched = TRUE; - } - LLInventoryModel::stopBackgroundFetch(); - } - } - gInventory.notifyObservers("fetchDescendents"); -} - -//static Bundle up a bunch of requests to send all at once. -void LLInventoryModel::bulkFetch(std::string url) -{ - //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. - //If there are items in sFetchQueue, 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. - //Stopbackgroundfetch will be run from the Responder instead of here. - - S16 max_concurrent_fetches=8; - F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely. - if (sMinTimeBetweenFetches < new_min_time) sMinTimeBetweenFetches=new_min_time; //HACK! See above. - - if(gDisconnected - || sBulkFetchCount > max_concurrent_fetches - || sFetchTimer.getElapsedTimeF32() < sMinTimeBetweenFetches) - { - return; // just bail if we are disconnected. - } - - U32 folder_count=0; - U32 max_batch_size=5; - - U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; - - LLSD body; - LLSD body_lib; - while( !(sFetchQueue.empty() ) && (folder_count < max_batch_size) ) - { - if (sFetchQueue.front().isNull()) //DEV-17797 - { - 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; - body["folders"].append(folder_sd); - folder_count++; - } - else - { - - - LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); - - if (cat) - { - if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - 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; - - if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) - body_lib["folders"].append(folder_sd); - else - body["folders"].append(folder_sd); - folder_count++; - } - if (sFullFetchStarted) - { //Already have this folder but append child folders to list. - // add all children to queue - parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != gInventory.mParentChildCategoryTree.end()) - { - cat_array_t* child_categories = cat_it->second; - - for (S32 child_num = 0; child_num < child_categories->count(); child_num++) - { - sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); - } - } - - } - } - } - sFetchQueue.pop_front(); - } - - if (folder_count > 0) - { - sBulkFetchCount++; - if (body["folders"].size()) - { - LLHTTPClient::post(url, body, new fetchDescendentsResponder(body),300.0); - } - if (body_lib["folders"].size()) - { - std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents"); - LLHTTPClient::post(url_lib, body_lib, new fetchDescendentsResponder(body_lib),300.0); - } - sFetchTimer.reset(); - } - else if (isBulkFetchProcessingComplete()) - { - if (sFullFetchStarted) - { - sAllFoldersFetched = TRUE; - } - stopBackgroundFetch(); - } -} - -// static -bool LLInventoryModel::isEverythingFetched() -{ - return (sAllFoldersFetched ? true : false); -} - -//static -BOOL LLInventoryModel::backgroundFetchActive() -{ - return sBackgroundFetchActive; -} - -//static -void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) -{ - if (!sAllFoldersFetched) - { - sBackgroundFetchActive = TRUE; - if (cat_id.isNull()) - { - if (!sFullFetchStarted) - { - sFullFetchStarted = TRUE; - sFetchQueue.push_back(gInventoryLibraryRoot); - sFetchQueue.push_back(gAgent.getInventoryRootID()); - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); - } - } - else - { - // specific folder requests go to front of queue - if (sFetchQueue.empty() || sFetchQueue.front() != cat_id) - { - sFetchQueue.push_front(cat_id); - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); - } - } - } -} - -//static -void LLInventoryModel::findLostItems() -{ - sBackgroundFetchActive = TRUE; - sFetchQueue.push_back(LLUUID::null); - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); -} - -//static -void LLInventoryModel::stopBackgroundFetch() -{ - if (sBackgroundFetchActive) - { - sBackgroundFetchActive = FALSE; - gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL); - sBulkFetchCount=0; - sMinTimeBetweenFetches=0.0f; -// sFullFetchStarted=FALSE; - } -} - -//static -void LLInventoryModel::backgroundFetch(void*) -{ - if (sBackgroundFetchActive && gAgent.getRegion()) - { - //If we'll be using the capability, we'll be sending batches and the background thing isn't as important. - std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents"); - if (!url.empty()) - { - bulkFetch(url); - return; - } - - //DEPRECATED OLD CODE FOLLOWS. - // no more categories to fetch, stop fetch process - if (sFetchQueue.empty()) - { - llinfos << "Inventory fetch completed" << llendl; - if (sFullFetchStarted) - { - sAllFoldersFetched = TRUE; - } - stopBackgroundFetch(); - return; - } - - F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f); - F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f); - if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time) - { - // double timeouts on failure - sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f); - sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f); - llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl; - // fetch is no longer considered "timely" although we will wait for full time-out - sTimelyFetchPending = FALSE; - } - - while(1) - { - if (sFetchQueue.empty()) - { - break; - } - - if(gDisconnected) - { - // just bail if we are disconnected. - break; - } - - LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); - - // category has been deleted, remove from queue. - if (!cat) - { - sFetchQueue.pop_front(); - continue; - } - - if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches && - LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - // category exists but has no children yet, fetch the descendants - // for now, just request every time and rely on retry timer to throttle - if (cat->fetchDescendents()) - { - sFetchTimer.reset(); - sTimelyFetchPending = TRUE; - } - else - { - // The catagory also tracks if it has expired and here it says it hasn't - // yet. Get out of here because nothing is going to happen until we - // update the timers. - break; - } - } - // do I have all my children? - else if (gInventory.isCategoryComplete(sFetchQueue.front())) - { - // finished with this category, remove from queue - sFetchQueue.pop_front(); - - // add all children to queue - parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != gInventory.mParentChildCategoryTree.end()) - { - cat_array_t* child_categories = cat_it->second; - - for (S32 child_num = 0; child_num < child_categories->count(); child_num++) - { - sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); - } - } - - // we received a response in less than the fast time - if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time) - { - // shrink timeouts based on success - sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f); - sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f); - //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl; - } - - sTimelyFetchPending = FALSE; - continue; - } - else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches) - { - // received first packet, but our num descendants does not match db's num descendants - // so try again later - LLUUID fetch_id = sFetchQueue.front(); - sFetchQueue.pop_front(); - - if (sNumFetchRetries++ < MAX_FETCH_RETRIES) - { - // push on back of queue - sFetchQueue.push_back(fetch_id); - } - sTimelyFetchPending = FALSE; - sFetchTimer.reset(); - break; - } - - // not enough time has elapsed to do a new fetch - break; - } - } + return cat->fetch(); } void LLInventoryModel::cache( @@ -1643,6 +1272,9 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) //llinfos << "LLInventoryModel::addCategory()" << llendl; if(category) { + // try to localize default names first. See EXT-8319, EXT-7051. + category->localizeName(); + // Insert category uniquely into the map mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one //mInventory[category->getUUID()] = category; @@ -1651,11 +1283,27 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) void LLInventoryModel::addItem(LLViewerInventoryItem* item) { - //llinfos << "LLInventoryModel::addItem()" << llendl; + llassert(item); if(item) { + // This can happen if assettype enums from llassettype.h ever change. + // For example, there is a known backwards compatibility issue in some viewer prototypes prior to when + // the AT_LINK enum changed from 23 to 24. + if ((item->getType() == LLAssetType::AT_NONE) + || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) + { + llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl; + return; + } + + // This condition means that we tried to add a link without the baseobj being in memory. + // The item will show up as a broken link. + if (item->getIsBrokenLink()) + { + llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl; + } + mItemMap[item->getUUID()] = item; - //mInventory[item->getUUID()] = item; } } @@ -1679,7 +1327,7 @@ void LLInventoryModel::empty() //mInventory.clear(); } -void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) +void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const { LLViewerInventoryCategory* cat = getCategory(update.mCategoryID); if(cat) @@ -1703,21 +1351,23 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - llinfos << "accounted: '" << cat->getName() << "' " - << version << " with " << descendents_actual - << " descendents." << llendl; + lldebugs << "accounted: '" << cat->getName() << "' " + << version << " with " << descendents_actual + << " descendents." << llendl; } } if(!accounted) { - lldebugs << "No accounting for: '" << cat->getName() << "' " + // 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. + llwarns << "Accounting failed for '" << cat->getName() << "' version:" << version << llendl; } } else { - llwarns << "No category found for update " << update.mCategoryID - << llendl; + llwarns << "No category found for update " << update.mCategoryID << llendl; } } @@ -1844,65 +1494,59 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const } bool LLInventoryModel::loadSkeleton( - const LLInventoryModel::options_t& options, + const LLSD& options, const LLUUID& owner_id) { lldebugs << "importing inventory skeleton for " << owner_id << llendl; typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t; cat_set_t temp_cats; - - update_map_t child_counts; - - LLUUID id; - LLAssetType::EType preferred_type; bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator skel; - skel = (*it).find("name"); - if(skel == no_response) goto clean_cat; - cat->rename(std::string((*skel).second)); - skel = (*it).find("folder_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - // if an id is null, it locks the viewer. - if(id.isNull()) goto clean_cat; - cat->setUUID(id); - skel = (*it).find("parent_id"); - if(skel == no_response) goto clean_cat; - id.set((*skel).second); - cat->setParent(id); - skel = (*it).find("type_default"); - if(skel == no_response) - { - preferred_type = LLAssetType::AT_NONE; + + for(LLSD::array_const_iterator it = options.beginArray(), + end = options.endArray(); it != end; ++it) + { + LLSD name = (*it)["name"]; + LLSD folder_id = (*it)["folder_id"]; + LLSD parent_id = (*it)["parent_id"]; + LLSD version = (*it)["version"]; + if(name.isDefined() + && folder_id.isDefined() + && parent_id.isDefined() + && version.isDefined() + && folder_id.asUUID().notNull() // if an id is null, it locks the viewer. + ) + { + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id); + cat->rename(name.asString()); + cat->setUUID(folder_id.asUUID()); + cat->setParent(parent_id.asUUID()); + + LLFolderType::EType preferred_type = LLFolderType::FT_NONE; + LLSD type_default = (*it)["type_default"]; + if(type_default.isDefined()) + { + preferred_type = (LLFolderType::EType)type_default.asInteger(); + } + cat->setPreferredType(preferred_type); + cat->setVersion(version.asInteger()); + temp_cats.insert(cat); } else { - S32 t = atoi((*skel).second.c_str()); - preferred_type = (LLAssetType::EType)t; + llwarns << "Unable to import near " << name.asString() << llendl; + rv = false; } - cat->setPreferredType(preferred_type); - skel = (*it).find("version"); - if(skel == no_response) goto clean_cat; - cat->setVersion(atoi((*skel).second.c_str())); - temp_cats.insert(cat); - continue; - clean_cat: - llwarns << "Unable to import near " << cat->getName() << llendl; - rv = false; - //delete cat; // automatic when cat is reasigned or destroyed } S32 cached_category_count = 0; S32 cached_item_count = 0; if(!temp_cats.empty()) { + update_map_t child_counts; cat_array_t categories; item_array_t items; + cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. std::string owner_id_str; owner_id.toString(owner_id_str); std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str)); @@ -1929,7 +1573,8 @@ bool LLInventoryModel::loadSkeleton( llinfos << "Unable to gunzip " << gzip_filename << llendl; } } - if(loadFromFile(inventory_filename, categories, items)) + bool is_cache_obsolete = false; + 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 @@ -1968,7 +1613,7 @@ bool LLInventoryModel::loadSkeleton( } // go ahead and add the cats returned during the download - std::set<LLUUID>::iterator not_cached_id = cached_ids.end(); + std::set<LLUUID>::const_iterator not_cached_id = cached_ids.end(); cached_category_count = cached_ids.size(); for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it) { @@ -1986,23 +1631,43 @@ bool LLInventoryModel::loadSkeleton( // Add all the items loaded which are parented to a // category with a correctly cached parent - count = items.count(); + S32 bad_link_count = 0; cat_map_t::iterator unparented = mCategoryMap.end(); - for(int i = 0; i < count; ++i) + for(item_array_t::const_iterator item_iter = items.begin(); + item_iter != items.end(); + ++item_iter) { - cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID()); + LLViewerInventoryItem *item = (*item_iter).get(); + const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID()); if(cit != unparented) { - LLViewerInventoryCategory* cat = cit->second; + const LLViewerInventoryCategory* cat = cit->second.get(); if(cat->getVersion() != NO_VERSION) { - addItem(items[i]); + // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. + if (item->getIsBrokenLink()) + { + bad_link_count++; + lldebugs << "Attempted to add cached link item without baseobj present ( name: " + << item->getName() << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; + invalid_categories.insert(cit->second); + continue; + } + addItem(item); cached_item_count += 1; ++child_counts[cat->getUUID()]; } } } + if (bad_link_count > 0) + { + llinfos << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << "The corresponding categories were invalidated." << llendl; + } } else { @@ -2016,20 +1681,31 @@ bool LLInventoryModel::loadSkeleton( } } + // Invalidate all categories that failed fetching descendents for whatever + // reason (e.g. one of the descendents was a broken link). + for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin(); + invalid_cat_it != invalid_categories.end(); + invalid_cat_it++) + { + LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); + cat->setVersion(NO_VERSION); + llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + } + // At this point, we need to set the known descendents for each // category which successfully cached so that we do not // needlessly fetch descendents for categories which we have. - update_map_t::iterator no_child_counts = child_counts.end(); - update_map_t::iterator the_count; + update_map_t::const_iterator no_child_counts = child_counts.end(); for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it) { - LLViewerInventoryCategory* cat = (*it); + LLViewerInventoryCategory* cat = (*it).get(); if(cat->getVersion() != NO_VERSION) { - the_count = child_counts.find(cat->getUUID()); + update_map_t::const_iterator the_count = child_counts.find(cat->getUUID()); if(the_count != no_child_counts) { - cat->setDescendentCount((*the_count).second.mValue); + const S32 num_descendents = (*the_count).second.mValue; + cat->setDescendentCount(num_descendents); } else { @@ -2043,6 +1719,12 @@ bool LLInventoryModel::loadSkeleton( // clean up the gunzipped file. LLFile::remove(inventory_filename); } + if(is_cache_obsolete) + { + // If out of date, remove the gzipped file too. + llwarns << "Inv cache out of date, removing" << llendl; + LLFile::remove(gzip_filename); + } categories.clear(); // will unref and delete entries } @@ -2053,89 +1735,6 @@ bool LLInventoryModel::loadSkeleton( return rv; } -bool LLInventoryModel::loadMeat( - const LLInventoryModel::options_t& options, const LLUUID& owner_id) -{ - llinfos << "importing inventory for " << owner_id << llendl; - LLPermissions default_perm; - default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); - LLPointer<LLViewerInventoryItem> item; - LLUUID id; - LLAssetType::EType type; - LLInventoryType::EType inv_type; - bool rv = true; - for(options_t::const_iterator it = options.begin(); it < options.end(); ++it) - { - item = new LLViewerInventoryItem; - response_t::const_iterator no_response = (*it).end(); - response_t::const_iterator meat; - meat = (*it).find("name"); - if(meat == no_response) goto clean_item; - item->rename(std::string((*meat).second)); - meat = (*it).find("item_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setUUID(id); - meat = (*it).find("parent_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - item->setParent(id); - meat = (*it).find("type"); - if(meat == no_response) goto clean_item; - type = (LLAssetType::EType)atoi((*meat).second.c_str()); - item->setType(type); - meat = (*it).find("inv_type"); - if(meat != no_response) - { - inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str()); - item->setInventoryType(inv_type); - } - meat = (*it).find("data_id"); - if(meat == no_response) goto clean_item; - id.set((*meat).second); - if(LLAssetType::AT_CALLINGCARD == type) - { - LLPermissions perm; - perm.init(id, owner_id, LLUUID::null, LLUUID::null); - item->setPermissions(perm); - } - else - { - meat = (*it).find("perm_mask"); - if(meat != no_response) - { - PermissionMask perm_mask = atoi((*meat).second.c_str()); - default_perm.initMasks( - perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); - } - else - { - default_perm.initMasks( - PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); - } - item->setPermissions(default_perm); - item->setAssetUUID(id); - } - meat = (*it).find("flags"); - if(meat != no_response) - { - item->setFlags(strtoul((*meat).second.c_str(), NULL, 0)); - } - meat = (*it).find("time"); - if(meat != no_response) - { - item->setCreationDate(atoi((*meat).second.c_str())); - } - addItem(item); - continue; - clean_item: - llwarns << "Unable to import near " << item->getName() << llendl; - rv = false; - //delete item; // automatic when item is reassigned or destroyed - } - return rv; -} - // This is a brute force method to rebuild the entire parent-child // relations. The overall operation has O(NlogN) performance, which // should be sufficient for our needs. @@ -2209,12 +1808,12 @@ void LLInventoryModel::buildParentChildMap() << cat->getName() << llendl; ++lost; // plop it into the lost & found. - LLAssetType::EType pref = cat->getPreferredType(); - if(LLAssetType::AT_NONE == pref) + LLFolderType::EType pref = cat->getPreferredType(); + if(LLFolderType::FT_NONE == pref) { - cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND)); + cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); } - else if(LLAssetType::AT_CATEGORY == pref) + else if(LLFolderType::FT_ROOT_INVENTORY == pref) { // it's the root cat->setParent(LLUUID::null); @@ -2222,7 +1821,7 @@ void LLInventoryModel::buildParentChildMap() else { // it's a protected folder. - cat->setParent(gAgent.getInventoryRootID()); + cat->setParent(gInventory.getRootFolderID()); } cat->updateServer(TRUE); catsp = getUnlockedCatArray(cat->getParentUUID()); @@ -2241,6 +1840,10 @@ void LLInventoryModel::buildParentChildMap() llwarns << "Found " << lost << " lost categories." << llendl; } + const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); + sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin(); + + // Now the items. We allocated in the last step, so now all we // have to do is iterate over the items and put them in the right // place. @@ -2256,7 +1859,7 @@ void LLInventoryModel::buildParentChildMap() } count = items.count(); lost = 0; - std::vector<LLUUID> lost_item_ids; + uuid_vec_t lost_item_ids; for(i = 0; i < count; ++i) { LLPointer<LLViewerInventoryItem> item; @@ -2273,7 +1876,7 @@ void LLInventoryModel::buildParentChildMap() ++lost; // plop it into the lost & found. // - item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND)); + item->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); // move it later using a special message to move items. If // we update server here, the client might crash. //item->updateServer(); @@ -2294,8 +1897,8 @@ void LLInventoryModel::buildParentChildMap() llwarns << "Found " << lost << " lost items." << llendl; LLMessageSystem* msg = gMessageSystem; BOOL start_new_message = TRUE; - LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); - for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it) + const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + for(uuid_vec_t::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it) { if(start_new_message) { @@ -2322,16 +1925,47 @@ void LLInventoryModel::buildParentChildMap() } } - const LLUUID& agent_inv_root_id = gAgent.getInventoryRootID(); + const LLUUID &agent_inv_root_id = gInventory.getRootFolderID(); if (agent_inv_root_id.notNull()) { cat_array_t* catsp = get_ptr_in_map(mParentChildCategoryTree, agent_inv_root_id); if(catsp) { + // *HACK - fix root inventory folder + // some accounts has pbroken inventory root folders + + std::string name = "My Inventory"; + LLUUID prev_root_id = mRootFolderID; + for (parent_cat_map_t::const_iterator it = mParentChildCategoryTree.begin(), + it_end = mParentChildCategoryTree.end(); it != it_end; ++it) + { + cat_array_t* cat_array = it->second; + for (cat_array_t::const_iterator cat_it = cat_array->begin(), + cat_it_end = cat_array->end(); cat_it != cat_it_end; ++cat_it) + { + LLPointer<LLViewerInventoryCategory> category = *cat_it; + + if(category && category->getPreferredType() != LLFolderType::FT_ROOT_INVENTORY) + continue; + if ( category && 0 == LLStringUtil::compareInsensitive(name, category->getName()) ) + { + if(category->getUUID()!=mRootFolderID) + { + LLUUID& new_inv_root_folder_id = const_cast<LLUUID&>(mRootFolderID); + new_inv_root_folder_id = category->getUUID(); + } + } + } + } + // 'My Inventory', // root of the agent's inv found. // The inv tree is built. mIsAgentInvUsable = true; + + llinfos << "Inventory initialized, notifying observers" << llendl; + addChangedMask(LLInventoryObserver::ALL, LLUUID::null); + notifyObservers(); } } } @@ -2368,77 +2002,11 @@ bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const return (mID > rhs.mID); } -// Given the current state of the inventory items, figure out the -// clone information. *FIX: This is sub-optimal, since we can insert -// this information snurgically, but this makes sure the implementation -// works before we worry about optimization. -//void LLInventoryModel::recalculateCloneInformation() -//{ -// //dumpInventory(); -// -// // This implements a 'multi-map' like structure to keep track of -// // how many clones we find. -// typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t; -// typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t; -// clone_map_t clone_map; -// LLUUIDAndName id_and_name; -// viewer_item_array_t* clones = NULL; -// LLViewerInventoryItem* item = NULL; -// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData(); -// item != NULL; -// item = (LLViewerInventoryItem*)mItemMap.getNextData()) -// { -// if(item->getType() == LLAssetType::AT_CALLINGCARD) -// { -// // if it's a calling card, we key off of the creator id, not -// // the asset id. -// id_and_name.mID = item->getCreatorUUID(); -// } -// else -// { -// // if it's not a calling card, we key clones from the -// // asset id. -// id_and_name.mID = item->getAssetUUID(); -// } -// if(id_and_name.mID == LLUUID::null) -// { -// continue; -// } -// id_and_name.mName = item->getName(); -// if(clone_map.checkData(id_and_name)) -// { -// clones = clone_map.getData(id_and_name); -// } -// else -// { -// clones = new viewer_item_array_t; -// clone_map.addData(id_and_name, clones); -// } -// clones->put(item); -// } -// -// S32 count = 0; -// for(clones = clone_map.getFirstData(); -// clones != NULL; -// clones = clone_map.getNextData()) -// { -// count = clones->count(); -// for(S32 i = 0; i < count; i++) -// { -// item = clones->get(i); -// item->setCloneCount(count - 1); -// //clones[i] = NULL; -// } -// delete clones; -// } -// clone_map.removeAllData(); -// //dumpInventory(); -//} - // static bool LLInventoryModel::loadFromFile(const std::string& filename, LLInventoryModel::cat_array_t& categories, - LLInventoryModel::item_array_t& items) + LLInventoryModel::item_array_t& items, + bool &is_cache_obsolete) { if(filename.empty()) { @@ -2455,11 +2023,32 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, // *NOTE: This buffer size is hard coded into scanf() below. char buffer[MAX_STRING]; /*Flawfinder: ignore*/ char keyword[MAX_STRING]; /*Flawfinder: ignore*/ + char value[MAX_STRING]; /*Flawfinder: ignore*/ + is_cache_obsolete = true; // Obsolete until proven current while(!feof(file) && fgets(buffer, MAX_STRING, file)) { - sscanf(buffer, " %254s", keyword); /* Flawfinder: ignore */ - if(0 == strcmp("inv_category", keyword)) + sscanf(buffer, " %126s %126s", keyword, value); /* Flawfinder: ignore */ + if(0 == strcmp("inv_cache_version", keyword)) { + S32 version; + int succ = sscanf(value,"%d",&version); + if ((1 == succ) && (version == sCurrentInvCacheVersion)) + { + // Cache is up to date + is_cache_obsolete = false; + continue; + } + else + { + // Cache is out of date + break; + } + } + else if(0 == strcmp("inv_category", keyword)) + { + if (is_cache_obsolete) + break; + LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null); if(inv_cat->importFileLocal(file)) { @@ -2473,6 +2062,9 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } else if(0 == strcmp("inv_item", keyword)) { + if (is_cache_obsolete) + break; + LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem; if( inv_item->importFileLocal(file) ) { @@ -2504,6 +2096,8 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } } fclose(file); + if (is_cache_obsolete) + return false; return true; } @@ -2525,6 +2119,7 @@ bool LLInventoryModel::saveToFile(const std::string& filename, return false; } + fprintf(file, "\tinv_cache_version\t%d\n",sCurrentInvCacheVersion); S32 count = categories.count(); S32 i; for(i = 0; i < count; ++i) @@ -2696,7 +2291,7 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) return; } S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - std::vector<LLUUID> item_ids; + uuid_vec_t item_ids; update_map_t update; for(S32 i = 0; i < count; ++i) { @@ -2712,7 +2307,7 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) } } gInventory.accountForUpdate(update); - for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it) + for(uuid_vec_t::iterator it = item_ids.begin(); it != item_ids.end(); ++it) { gInventory.deleteObject(*it); } @@ -2743,7 +2338,7 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, lastfolder = tfolder; tfolder->unpackMessage(msg, _PREHASH_FolderData, i); // make sure it's not a protected folder - tfolder->setPreferredType(LLAssetType::AT_NONE); + tfolder->setPreferredType(LLFolderType::FT_NONE); folders.push_back(tfolder); // examine update for changes. LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); @@ -2772,10 +2367,10 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, gInventory.notifyObservers(); // *HACK: Do the 'show' logic for a new item in the inventory. - LLInventoryView* view = LLInventoryView::getActiveInventory(); - if(view) + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) { - view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); + active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); } } @@ -2792,7 +2387,7 @@ void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, << llendl; return; } - std::vector<LLUUID> folder_ids; + uuid_vec_t folder_ids; update_map_t update; S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); for(S32 i = 0; i < count; ++i) @@ -2806,7 +2401,7 @@ void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, } } gInventory.accountForUpdate(update); - for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it) + for(uuid_vec_t::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it) { gInventory.deleteObject(*it); } @@ -2873,7 +2468,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) } LLUUID tid; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid); +#ifndef LL_RELEASE_FOR_DOWNLOAD llinfos << "Bulk inventory: " << tid << llendl; +#endif update_map_t update; cat_array_t folders; @@ -2919,7 +2516,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); - std::vector<LLUUID> wearable_ids; + uuid_vec_t wearable_ids; item_array_t items; std::list<InventoryCallbackInfo> cblist; for(i = 0; i < count; ++i) @@ -2981,20 +2578,20 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) // The incoming inventory could span more than one BulkInventoryUpdate packet, // so record the transaction ID for this purchase, then wear all clothing // that comes in as part of that transaction ID. JC - if (LLInventoryView::sWearNewClothing) + if (LLInventoryState::sWearNewClothing) { - LLInventoryView::sWearNewClothingTransactionID = tid; - LLInventoryView::sWearNewClothing = FALSE; + LLInventoryState::sWearNewClothingTransactionID = tid; + LLInventoryState::sWearNewClothing = FALSE; } - if (tid == LLInventoryView::sWearNewClothingTransactionID) + if (tid == LLInventoryState::sWearNewClothingTransactionID) { count = wearable_ids.size(); for (i = 0; i < count; ++i) { LLViewerInventoryItem* wearable_item; wearable_item = gInventory.getItem(wearable_ids[i]); - wear_inventory_item_on_avatar(wearable_item); + LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); } } @@ -3005,7 +2602,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID); } // Don't show the inventory. We used to call showAgentInventory here. - //LLInventoryView* view = LLInventoryView::getActiveInventory(); + //LLFloaterInventory* view = LLFloaterInventory::getActiveInventory(); //if(view) //{ // const BOOL take_keyboard_focus = FALSE; @@ -3015,10 +2612,10 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) // // HACK to open inventory offers that are accepted. This information // // really needs to flow through the instant messages and inventory // // transfer/update messages. - // if (LLInventoryView::sOpenNextNewItem) + // if (LLFloaterInventory::sOpenNextNewItem) // { // view->openSelected(); - // LLInventoryView::sOpenNextNewItem = FALSE; + // LLFloaterInventory::sOpenNextNewItem = FALSE; // } // // // restore keyboard focus @@ -3033,8 +2630,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a UpdateInventoryItem for the wrong agent." - << llendl; + llwarns << "Got a UpdateInventoryItem for the wrong agent." << llendl; return; } LLUUID parent_id; @@ -3045,6 +2641,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) msg->getS32("AgentData", "Version", version); S32 descendents; msg->getS32("AgentData", "Descendents", descendents); + S32 i; S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id); @@ -3059,6 +2656,12 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) for(i = 0; i < count; ++i) { titem->unpackMessage(msg, _PREHASH_ItemData, i); + // 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())) + { + lldebugs << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << llendl; + continue; + } gInventory.updateItem(titem); } @@ -3068,6 +2671,9 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) { cat->setVersion(version); cat->setDescendentCount(descendents); + // Get this UUID on the changed list so that whatever's listening for it + // will get triggered. + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat->getUUID()); } gInventory.notifyObservers(); } @@ -3130,31 +2736,31 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**) //---------------------------------------------------------------------------- -// Trash: LLAssetType::AT_TRASH, "ConfirmEmptyTrash" -// Lost&Found: LLAssetType::AT_LOST_AND_FOUND, "ConfirmEmptyLostAndFound" +// Trash: LLFolderType::FT_TRASH, "ConfirmEmptyTrash" +// Lost&Found: LLFolderType::FT_LOST_AND_FOUND, "ConfirmEmptyLostAndFound" -bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLAssetType::EType folder_type) +bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLFolderType::EType preferred_type) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) // YES { - LLUUID folder_id = findCategoryUUIDForType(folder_type); + const LLUUID folder_id = findCategoryUUIDForType(preferred_type); purgeDescendentsOf(folder_id); notifyObservers(); } return false; } -void LLInventoryModel::emptyFolderType(const std::string notification, LLAssetType::EType folder_type) +void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderType::EType preferred_type) { if (!notification.empty()) { - LLNotifications::instance().add(notification, LLSD(), LLSD(), - boost::bind(&LLInventoryModel::callbackEmptyFolderType, this, _1, _2, folder_type)); + LLNotificationsUtil::add(notification, LLSD(), LLSD(), + boost::bind(&LLInventoryModel::callbackEmptyFolderType, this, _1, _2, preferred_type)); } else { - LLUUID folder_id = findCategoryUUIDForType(folder_type); + const LLUUID folder_id = findCategoryUUIDForType(preferred_type); purgeDescendentsOf(folder_id); notifyObservers(); } @@ -3165,7 +2771,7 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLAssetTy void LLInventoryModel::removeItem(const LLUUID& item_id) { LLViewerInventoryItem* item = getItem(item_id); - const LLUUID& new_parent = findCategoryUUIDForType(LLAssetType::AT_TRASH); + const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH); if (item && item->getParentUUID() != new_parent) { LLInventoryModel::update_list_t update; @@ -3183,743 +2789,186 @@ void LLInventoryModel::removeItem(const LLUUID& item_id) } } -//---------------------------------------------------------------------------- - -// *NOTE: DEBUG functionality -void LLInventoryModel::dumpInventory() +const LLUUID &LLInventoryModel::getRootFolderID() const { - llinfos << "\nBegin Inventory Dump\n**********************:" << llendl; - llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl; - for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) - { - LLViewerInventoryCategory* cat = cit->second; - if(cat) - { - llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' " - << cat->getVersion() << " " << cat->getDescendentCount() - << llendl; - } - else - { - llinfos << " NULL!" << llendl; - } - } - llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl; - for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) - { - LLViewerInventoryItem* item = iit->second; - if(item) - { - llinfos << " " << item->getUUID() << " " - << item->getName() << llendl; - } - else - { - llinfos << " NULL!" << llendl; - } - } - llinfos << "\n**********************\nEnd Inventory Dump" << llendl; + return mRootFolderID; } -///---------------------------------------------------------------------------- -/// LLInventoryCollectFunctor implementations -///---------------------------------------------------------------------------- - -// static -bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(LLInventoryItem* item) +void LLInventoryModel::setRootFolderID(const LLUUID& val) { - if (!item) - return false; - - bool allowed = false; - LLVOAvatarSelf* my_avatar = NULL; - - switch(item->getType()) - { - case LLAssetType::AT_CALLINGCARD: - // not allowed - break; - - case LLAssetType::AT_OBJECT: - my_avatar = gAgent.getAvatarObject(); - if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID())) - { - allowed = true; - } - break; - - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - if(!gAgentWearables.isWearingItem(item->getUUID())) - { - allowed = true; - } - break; - - default: - allowed = true; - break; - } - - return allowed; + mRootFolderID = val; } -bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +const LLUUID &LLInventoryModel::getLibraryRootFolderID() const { - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) return TRUE; - } - if(item) - { - if(item->getType() == mType) return TRUE; - } - return FALSE; + return mLibraryRootFolderID; } -bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +void LLInventoryModel::setLibraryRootFolderID(const LLUUID& val) { - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) return FALSE; - } - if(item) - { - if(item->getType() == mType) return FALSE; - else return TRUE; - } - return TRUE; + mLibraryRootFolderID = val; } -bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +const LLUUID &LLInventoryModel::getLibraryOwnerID() const { - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) - { - return TRUE; - } - } - if(item) - { - if(item->getType() == mType) - { - LLPermissions perm = item->getPermissions(); - if ((perm.getMaskBase() & mPerm) == mPerm) - { - return TRUE; - } - } - } - return FALSE; + return mLibraryOwnerID; } - -//bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -//{ -// if(cat) return FALSE; -// if(item) -// { -// if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD) -// { -// if((item->getType() == LLAssetType::AT_CALLINGCARD) -// && !(item->getCreatorUUID().isNull()) -// && (item->getCreatorUUID() == mItemMap->getCreatorUUID())) -// { -// return TRUE; -// } -// } -// else -// { -// if((item->getType() == mItemMap->getType()) -// && !(item->getAssetUUID().isNull()) -// && (item->getAssetUUID() == mItemMap->getAssetUUID()) -// && (item->getName() == mItemMap->getName())) -// { -// return TRUE; -// } -// } -// } -// return FALSE; -//} - -bool LLBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) +void LLInventoryModel::setLibraryOwnerID(const LLUUID& val) { - if(item) - { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (!item->getCreatorUUID().isNull()) - && (item->getCreatorUUID() != gAgent.getID())) - { - return true; - } - } - return false; + mLibraryOwnerID = val; } - -bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) +// static +BOOL LLInventoryModel::getIsFirstTimeInViewer2() { - if(item) + // Do not call this before parentchild map is built. + if (!gInventory.mIsAgentInvUsable) { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (item->getCreatorUUID().notNull()) - && (item->getCreatorUUID() != gAgent.getID())) - { - mSeen.insert(item->getCreatorUUID()); - return true; - } + llwarns << "Parent Child Map not yet built; guessing as first time in viewer2." << llendl; + return TRUE; } - return false; -} - -bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (item->getCreatorUUID() == mBuddyID)) - { - return TRUE; - } - } - return FALSE; + return sFirstTimeInViewer2; } - -bool LLNameCategoryCollector::operator()( - LLInventoryCategory* cat, LLInventoryItem* item) +static LLInventoryModel::item_array_t::iterator find_item_iter_by_uuid(LLInventoryModel::item_array_t& items, const LLUUID& id) { - if(cat) - { - if (!LLStringUtil::compareInsensitive(mName, cat->getName())) - { - return true; - } - } - return false; -} + LLInventoryModel::item_array_t::iterator result = items.end(); - - -///---------------------------------------------------------------------------- -/// Observers -///---------------------------------------------------------------------------- - -void LLInventoryCompletionObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mIncomplete.empty()) + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) + if ((*i)->getUUID() == id) { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - it = mIncomplete.erase(it); - continue; - } - if(item->isComplete()) - { - mComplete.push_back(*it); - it = mIncomplete.erase(it); - continue; - } - ++it; - } - if(mIncomplete.empty()) - { - done(); + result = i; + break; } } + + return result; } -void LLInventoryCompletionObserver::watchItem(const LLUUID& id) +// static +// * @param[in, out] items - vector with items to be updated. It should be sorted in a right way +// * before calling this method. +// * @param src_item_id - LLUUID of inventory item to be moved in new position +// * @param dest_item_id - LLUUID of inventory item before which source item should be placed. +void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id) { - if(id.notNull()) - { - mIncomplete.push_back(id); - } -} + LLInventoryModel::item_array_t::iterator it_src = find_item_iter_by_uuid(items, src_item_id); + LLInventoryModel::item_array_t::iterator it_dest = find_item_iter_by_uuid(items, dest_item_id); + if (it_src == items.end() || it_dest == items.end()) return; -void LLInventoryFetchObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mIncomplete.empty()) - { - for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - // BUG: This can cause done() to get called prematurely below. - // This happens with the LLGestureInventoryFetchObserver that - // loads gestures at startup. JC - it = mIncomplete.erase(it); - continue; - } - if(item->isComplete()) - { - mComplete.push_back(*it); - it = mIncomplete.erase(it); - continue; - } - ++it; - } - if(mIncomplete.empty()) - { - done(); - } - } - //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl; - //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl; + LLViewerInventoryItem* src_item = *it_src; + items.erase(it_src); + + // target iterator can not be valid because the container was changed, so update it. + it_dest = find_item_iter_by_uuid(items, dest_item_id); + items.insert(it_dest, src_item); } -bool LLInventoryFetchObserver::isEverythingComplete() const +//* @param[in] items vector of items in order to be saved. +void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) { - return mIncomplete.empty(); -} + int sortField = 0; -void fetch_items_from_llsd(const LLSD& items_llsd) -{ - if (!items_llsd.size()) return; - LLSD body; - body[0]["cap_name"] = "FetchInventory"; - body[1]["cap_name"] = "FetchLib"; - for (S32 i=0; i<items_llsd.size();i++) - { - if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) - { - body[0]["items"].append(items_llsd[i]); - continue; - } - if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString()) - { - body[1]["items"].append(items_llsd[i]); - continue; - } - } - - for (S32 i=0; i<body.size(); i++) + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) { - if (0 >= body[i].size()) continue; - std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); + LLViewerInventoryItem* item = *i; - if (!url.empty()) - { - body[i]["agent_id"] = gAgent.getID(); - LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); - break; - } + item->setSortField(++sortField); + item->setComplete(TRUE); + item->updateServer(FALSE); - LLMessageSystem* msg = gMessageSystem; - BOOL start_new_message = TRUE; - for (S32 j=0; j<body[i]["items"].size(); j++) - { - LLSD item_entry = body[i]["items"][j]; - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_FetchInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); - msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); - if(msg->isSendFull(NULL)) - { - start_new_message = TRUE; - gAgent.sendReliableMessage(); - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - } - } -} + updateItem(item); -void LLInventoryFetchObserver::fetchItems( - const LLInventoryFetchObserver::item_ref_t& ids) -{ - LLUUID owner_id; - LLSD items_llsd; - for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(item) - { - if(item->isComplete()) - { - // It's complete, so put it on the complete container. - mComplete.push_back(*it); - continue; - } - else - { - owner_id = item->getPermissions().getOwner(); - } - } - else - { - // assume it's agent inventory. - owner_id = gAgent.getID(); - } - - // It's incomplete, so put it on the incomplete container, and - // pack this on the message. - mIncomplete.push_back(*it); - - // Prepare the data to fetch - LLSD item_entry; - item_entry["owner_id"] = owner_id; - item_entry["item_id"] = (*it); - items_llsd.append(item_entry); + // Tell the parent folder to refresh its sort order. + addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); } - fetch_items_from_llsd(items_llsd); -} -// virtual -void LLInventoryFetchDescendentsObserver::changed(U32 mask) -{ - for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) - { - it = mIncompleteFolders.erase(it); - continue; - } - if(isComplete(cat)) - { - mCompleteFolders.push_back(*it); - it = mIncompleteFolders.erase(it); - continue; - } - ++it; - } - if(mIncompleteFolders.empty()) - { - done(); - } + notifyObservers(); } -void LLInventoryFetchDescendentsObserver::fetchDescendents( - const folder_ref_t& ids) +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort { - for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it) +public: + bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) continue; - if(!isComplete(cat)) - { - cat->fetchDescendents(); //blindly fetch it without seeing if anything else is fetching it. - mIncompleteFolders.push_back(*it); //Add to list of things being downloaded for this observer. - } - else - { - mCompleteFolders.push_back(*it); - } + return a->getSortField() < b->getSortField(); } -} +}; -bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const +/** + * Sorts passed items by LLViewerInventoryItem sort field. + * + * @param[in, out] items - array of items, not sorted. + */ +static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) { - return mIncompleteFolders.empty(); + static LLViewerInventoryItemSort sort_functor; + std::sort(items.begin(), items.end(), sort_functor); } -bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) { - S32 version = cat->getVersion(); - S32 descendents = cat->getDescendentCount(); - if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) - || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) - { - return false; - } - // it might be complete - check known descendents against - // currently available. - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); - if(!cats || !items) - { - // bit of a hack - pretend we're done if they are gone or - // incomplete. should never know, but it would suck if this - // kept tight looping because of a corrupt memory state. - return true; - } - S32 known = cats->count() + items->count(); - if(descendents == known) - { - // hey - we're done. - return true; - } - return false; -} + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); -void LLInventoryFetchComboObserver::changed(U32 mask) -{ - if(!mIncompleteItems.empty()) - { - for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - it = mIncompleteItems.erase(it); - continue; - } - if(item->isComplete()) - { - mCompleteItems.push_back(*it); - it = mIncompleteItems.erase(it); - continue; - } - ++it; - } - } - if(!mIncompleteFolders.empty()) - { - for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) - { - it = mIncompleteFolders.erase(it); - continue; - } - if(gInventory.isCategoryComplete(*it)) - { - mCompleteFolders.push_back(*it); - it = mIncompleteFolders.erase(it); - continue; - } - ++it; - } - } - if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty()) - { - mDone = true; - done(); - } -} + // ensure items are sorted properly before changing order. EXT-3498 + rearrange_item_order_by_sort_field(items); -void LLInventoryFetchComboObserver::fetch( - const folder_ref_t& folder_ids, - const item_ref_t& item_ids) -{ - lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl; - for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*fit); - if(!cat) continue; - if(!gInventory.isCategoryComplete(*fit)) - { - cat->fetchDescendents(); - lldebugs << "fetching folder " << *fit <<llendl; - mIncompleteFolders.push_back(*fit); - } - else - { - mCompleteFolders.push_back(*fit); - lldebugs << "completing folder " << *fit <<llendl; - } - } + // update order + updateItemsOrder(items, source_item_id, target_item_id); - // Now for the items - we fetch everything which is not a direct - // descendent of an incomplete folder because the item will show - // up in an inventory descendents message soon enough so we do not - // have to fetch it individually. - LLSD items_llsd; - LLUUID owner_id; - for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) - { - LLViewerInventoryItem* item = gInventory.getItem(*iit); - if(!item) - { - lldebugs << "uanble to find item " << *iit << llendl; - continue; - } - if(item->isComplete()) - { - // It's complete, so put it on the complete container. - mCompleteItems.push_back(*iit); - lldebugs << "completing item " << *iit << llendl; - continue; - } - else - { - mIncompleteItems.push_back(*iit); - owner_id = item->getPermissions().getOwner(); - } - if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) - { - LLSD item_entry; - item_entry["owner_id"] = owner_id; - item_entry["item_id"] = (*iit); - items_llsd.append(item_entry); - } - else - { - lldebugs << "not worrying about " << *iit << llendl; - } - } - fetch_items_from_llsd(items_llsd); + saveItemsOrder(items); } -void LLInventoryExistenceObserver::watchItem(const LLUUID& id) -{ - if(id.notNull()) - { - mMIA.push_back(id); - } -} +//---------------------------------------------------------------------------- -void LLInventoryExistenceObserver::changed(U32 mask) +// *NOTE: DEBUG functionality +void LLInventoryModel::dumpInventory() const { - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mMIA.empty()) + llinfos << "\nBegin Inventory Dump\n**********************:" << llendl; + llinfos << "mCategory[] contains " << mCategoryMap.size() << " items." << llendl; + for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) { - for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); ) + const LLViewerInventoryCategory* cat = cit->second; + if(cat) { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - ++it; - continue; - } - mExist.push_back(*it); - it = mMIA.erase(it); + llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' " + << cat->getVersion() << " " << cat->getDescendentCount() + << llendl; } - if(mMIA.empty()) + else { - done(); + llinfos << " NULL!" << llendl; } - } -} - -void LLInventoryAddedObserver::changed(U32 mask) -{ - if(!(mask & LLInventoryObserver::ADD)) - { - 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; - if (mMessageName.empty()) - { - msg_name = msg->getMessageName(); - } - else - { - msg_name = mMessageName; - } - - if (msg_name.empty()) - { - return; - } - - // We only want newly created inventory items. JC - if ( msg_name != "UpdateCreateInventoryItem") - { - return; - } - - LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - for(S32 i = 0; i < num_blocks; ++i) + } + llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl; + for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) { - titem->unpackMessage(msg, _PREHASH_InventoryData, i); - if (!(titem->getUUID().isNull())) + const LLViewerInventoryItem* item = iit->second; + if(item) { - //we don't do anything with null keys - mAdded.push_back(titem->getUUID()); + llinfos << " " << item->getUUID() << " " + << item->getName() << llendl; } - } - if (!mAdded.empty()) - { - done(); - } -} - -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 - folder_ref_t folders; - item_ref_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); - } + else + { + llinfos << " NULL!" << llendl; } } + llinfos << "\n**********************\nEnd Inventory Dump" << llendl; } - -///---------------------------------------------------------------------------- -/// LLAssetIDMatches -///---------------------------------------------------------------------------- -bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - return (item && item->getAssetUUID() == mAssetID); -} - - ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- |